{"id":28146,"date":"2026-06-10T07:18:06","date_gmt":"2026-06-10T07:18:06","guid":{"rendered":"https:\/\/www.stroople.com\/?page_id=28146"},"modified":"2026-09-20T16:06:49","modified_gmt":"2026-09-20T16:06:49","slug":"signal","status":"publish","type":"page","link":"https:\/\/www.stroople.com\/fr\/signal\/","title":{"rendered":"Signal Stroople"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"28146\" class=\"elementor elementor-28146\" wpc-filter-elementor-widget=\"1\" data-elementor-post-type=\"page\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-619ed34 elementor-section-full_width elementor-section-height-default elementor-section-height-default syd-sticky-section-no\" data-id=\"619ed34\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;background_background&quot;:&quot;gradient&quot;,&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-357d32b syd-sticky-section-no\" data-id=\"357d32b\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-5aa9821 syd-sticky-section-no elementor-widget elementor-widget-html\" data-id=\"5aa9821\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Signal Dashboard<\/title>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Montserrat:wght@400;600;700&display=swap\" rel=\"stylesheet\">\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/d3\/7.9.0\/d3.min.js\"><\/script>\n<style>\n#ss-ticker-wrap{background:#04014a;padding:9px 24px;display:flex;align-items:center;gap:14px;overflow:hidden;width:100%;box-sizing:border-box;border-top:1px solid rgba(255,255,255,0.1);}\n.ss-live-badge{display:inline-flex;align-items:center;gap:6px;background:#ef4444;color:#fff;font-size:10px;font-weight:700;letter-spacing:0.06em;padding:4px 9px;border-radius:4px;flex-shrink:0;font-family:Montserrat,sans-serif;}\n.ss-live-dot{width:6px;height:6px;border-radius:50%;background:#fff;flex-shrink:0;animation:ss-live-pulse 1.4s ease-in-out infinite;}\n@keyframes ss-live-pulse{0%,100%{opacity:1;transform:scale(1);}50%{opacity:0.35;transform:scale(0.7);}}\n.ss-ticker-tag{font-size:10px;font-weight:600;color:#00A7C4;letter-spacing:0.1em;text-transform:uppercase;white-space:nowrap;flex-shrink:0}\n.ss-ticker-sep{width:1px;height:13px;background:rgba(255,255,255,0.15);flex-shrink:0}\n.ss-ticker-track{overflow:hidden;flex:1;mask-image:linear-gradient(to right,transparent,black 5%,black 95%,transparent);-webkit-mask-image:linear-gradient(to right,transparent,black 5%,black 95%,transparent)}\n.ss-ticker-inner{display:inline-flex;gap:48px;white-space:nowrap;animation:ss-ticker-scroll 40s linear infinite}\n.ss-ticker-inner:hover{animation-play-state:paused}\n@keyframes ss-ticker-scroll{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}\n.ss-ticker-item{font-size:12px;color:rgba(255,255,255,0.65);display:inline-flex;align-items:center;gap:7px;flex-shrink:0}\n.ss-ticker-item a{color:rgba(255,255,255,0.65);text-decoration:none}\n.ss-ticker-item a:hover{color:#fff}\n.ss-ticker-dot{width:5px;height:5px;border-radius:50%;display:inline-block;flex-shrink:0}\n.ss-dot-critical{background:#ef4444}\n.ss-dot-warning{background:#f59e0b}\n.ss-dot-info{background:#00A7C4}\n\n.ss-bar-wrapper {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: flex-end;\n  height: 100%;\n}\n.ss-bar-value {\n  font-size: 10px;\n  font-weight: 700;\n  color: #09025F;\n  margin-bottom: 2px;\n  line-height: 1;\n}\n.ss-flag-fr {\n  display: inline-block;\n  vertical-align: middle;\n  margin-left: 6px;\n  border-radius: 2px;\n  box-shadow: 0 0 1px rgba(0,0,0,0.3);\n}\n\n.ss-sidebar-toggle-btn{display:none;background:#09025F;color:#fff;font-family:Montserrat,sans-serif;font-size:13px;font-weight:600;padding:10px 16px;border:none;border-radius:6px;margin:16px 24px 0;cursor:pointer;}\n@media (max-width:900px){\n  #ss-sidebar{\n    position:fixed !important;top:0;left:0;height:100vh;width:260px;\n    background:#fff;z-index:999;box-shadow:2px 0 12px rgba(0,0,0,0.15);\n    transform:translateX(-100%);transition:transform .25s;overflow-y:auto;\n  }\n  #ss-sidebar.ss-open{transform:translateX(0);}\n  .ss-sidebar-toggle-btn{display:inline-block;}\n}\n\n.ss-hub-constellation-card{background:#fff;border-radius:16px;padding:26px 28px;margin:28px 0 0;box-shadow:0 1px 3px rgba(0,0,0,0.06);border:1px solid #ececf2;}\n.ss-hub-constellation-header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px;flex-wrap:wrap;gap:6px;}\n.ss-hub-constellation-title{font-family:Montserrat,sans-serif;font-weight:700;font-size:16px;color:#09025F;}\n.ss-hub-constellation-sub{font-family:Montserrat,sans-serif;font-size:12px;color:#8a8a9a;}\n.ss-hub-constellation-legend{display:flex;flex-wrap:wrap;gap:14px;margin:14px 0;font-family:Montserrat,sans-serif;}\n.ss-hub-constellation-legend-item{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;color:#5a5a6e;}\n.ss-hub-constellation-legend-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0;}\n.ss-hub-constellation-svg-wrap{position:relative;width:100%;}\n.ss-hub-constellation-svg-wrap svg{width:100%;height:auto;display:block;}\n.ss-hub-constellation-node{cursor:pointer;}\n.ss-hub-constellation-node.ss-frozen{stroke:#09025F;stroke-width:2.25;}\n.ss-hub-constellation-label{font-family:Montserrat,sans-serif;font-size:10.5px;font-weight:700;fill:#09025F;pointer-events:none;}\n.ss-hub-constellation-label-bg{fill:#fff;fill-opacity:0.8;pointer-events:none;}\n.ss-hub-constellation-tooltip{position:absolute;z-index:20;pointer-events:none;background:#04014a;color:#fff;font-family:Montserrat,sans-serif;font-size:12px;line-height:1.5;padding:10px 12px;border-radius:8px;max-width:260px;box-shadow:0 4px 14px rgba(0,0,0,0.18);opacity:0;transition:opacity .12s;}\n.ss-hub-constellation-tooltip.ss-visible{opacity:1;}\n.ss-hub-constellation-tooltip strong{color:#fff;}\n.ss-hub-constellation-tooltip .ss-ctt-sub{color:#c7c7e6;font-size:11px;}\n.ss-hub-constellation-detail{position:absolute;z-index:30;top:10px;right:10px;width:240px;background:#fff;border:1px solid #ececf2;border-radius:12px;padding:14px 16px;box-shadow:0 4px 18px rgba(0,0,0,0.12);font-family:Montserrat,sans-serif;display:none;}\n.ss-hub-constellation-detail.ss-visible{display:block;}\n.ss-hub-constellation-detail-close{position:absolute;top:8px;right:10px;border:none;background:none;color:#a0a0b0;font-size:15px;font-weight:700;cursor:pointer;line-height:1;padding:2px;}\n.ss-hub-constellation-detail-close:hover{color:#5a5a6e;}\n.ss-hub-constellation-detail-title{font-size:13.5px;font-weight:700;color:#09025F;padding-right:16px;}\n.ss-hub-constellation-detail-sub{font-size:11px;color:#8a8a9a;margin-top:3px;}\n.ss-hub-constellation-detail-body{font-size:12px;color:#5a5a6e;line-height:1.55;margin-top:8px;}\n.ss-hub-constellation-detail-link{display:inline-block;margin-top:10px;font-size:11.5px;font-weight:700;color:#09025F;text-decoration:none;}\n.ss-hub-constellation-detail-link:hover{text-decoration:underline;}\n@media (max-width:640px){\n  .ss-hub-constellation-detail{position:static;width:auto;margin-top:12px;}\n}\n<\/style>\n<\/head>\n<body>\n\n<div style=\"font-family:Montserrat,-apple-system,sans-serif;\">\n\n<div id=\"ss-topbar\" style=\"background:#04014a;padding:10px 24px;display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:14px 20px;box-sizing:border-box;\">\n  <a href=\"https:\/\/www.stroople.com\/signal-about-the-data\/\" style=\"color:#fff;text-decoration:none;display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:14px;opacity:0.85;\">About the data<\/a>\n  <a href=\"https:\/\/www.stroople.com\/\" target=\"_blank\" rel=\"noopener\" style=\"color:#fff;text-decoration:none;display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:14px;opacity:0.85;\">\n    Stroople\n    <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\"><path d=\"M7 17L17 7M17 7H8M17 7V16\"\/><\/svg>\n  <\/a>\n<\/div>\n\n<!-- LIVE CERT-FR \/ ANSSI TICKER -->\n<div id=\"ss-ticker-wrap\">\n  <span class=\"ss-live-badge\">\n    <span class=\"ss-live-dot\"><\/span>\n    LIVE\n  <\/span>\n  <span class=\"ss-ticker-tag\">CERT-FR&nbsp;\/&nbsp;ANSSI<\/span>\n  <div class=\"ss-ticker-sep\"><\/div>\n  <div class=\"ss-ticker-track\">\n    <div class=\"ss-ticker-inner\" id=\"ss-ticker-inner\">\n      <span class=\"ss-ticker-item\">\n        <span class=\"ss-ticker-dot ss-dot-info\"><\/span>\n        Loading official security alerts...\n      <\/span>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  var cfg=(typeof SS_CONFIG!=='undefined')?SS_CONFIG:{ajax_url:'\/wp-admin\/admin-ajax.php',nonce:''};\n  function esc(str){var d=document.createElement('div');d.textContent=str||'';return d.innerHTML;}\n  function fmt_age(ts){\n    if(!ts) return '';\n    var diff=Math.floor((Date.now()\/1000)-ts);\n    if(diff<3600) return Math.floor(diff\/60)+'min ago';\n    if(diff<86400) return Math.floor(diff\/3600)+'h ago';\n    return Math.floor(diff\/86400)+'d ago';\n  }\n  function render(items){\n    var inner=document.getElementById('ss-ticker-inner');\n    if(!inner||!items||!items.length) return;\n    var doubled=items.concat(items);\n    var html=doubled.map(function(item){\n      var dot='ss-dot-info';\n      if(item.severity==='critical') dot='ss-dot-critical';\n      else if(item.severity==='warning') dot='ss-dot-warning';\n      var age=item.date?' <span style=\"opacity:0.4;font-size:11px\">'+fmt_age(item.date)+'<\/span>':'';\n      var link=item.link?'<a href=\"'+esc(item.link)+'\" target=\"_blank\" rel=\"noopener\">'+esc(item.title)+'<\/a>':esc(item.title);\n      return '<span class=\"ss-ticker-item\"><span class=\"ss-ticker-dot '+dot+'\"><\/span>'+link+age+'<\/span>';\n    }).join('');\n    inner.innerHTML=html;\n    var duration=Math.max(30,items.length*6);\n    inner.style.animationDuration=duration+'s';\n  }\n  function load(){\n    var url=cfg.ajax_url+'?action=ss_get_data&nonce='+cfg.nonce+'&section=certfr';\n    fetch(url)\n      .then(function(r){return r.json();})\n      .then(function(res){\n        if(res.success&&res.data&&res.data.certfr&&res.data.certfr.items){\n          render(res.data.certfr.items);\n        }\n      })\n      .catch(function(){\n        var inner=document.getElementById('ss-ticker-inner');\n        if(inner) inner.innerHTML='<span class=\"ss-ticker-item\"><span class=\"ss-ticker-dot ss-dot-warning\"><\/span>CERT-FR feed temporarily unavailable<\/span>';\n      });\n  }\n  if(document.readyState==='loading'){\n    document.addEventListener('DOMContentLoaded',load);\n  } else {\n    load();\n  }\n})();\n\n\/\/ Observer DOM\ndocument.addEventListener('DOMContentLoaded', function(){\n  var observer = new MutationObserver(function(){\n    var crisisElem = document.getElementById('ss-hub-crisisroom');\n    if(crisisElem){\n      var titleElem = crisisElem.querySelector('h2, .ss-title, .ss-card-title, h3');\n      if(titleElem && titleElem.textContent.toLowerCase().includes('crisis room')){\n        titleElem.textContent = titleElem.textContent.replace(\/Crisis Room\/i, 'War Room');\n      }\n      var links = crisisElem.querySelectorAll('a, button');\n      links.forEach(function(el){\n        var txt = el.textContent.toLowerCase();\n        if(txt.includes('ransomware tracker') || txt.includes('view ransomware')){\n          el.remove();\n        }\n      });\n    }\n\n    var indicators = document.getElementById('ss-hub-indicators');\n    if(indicators){\n      var cards = Array.from(indicators.children);\n      cards.forEach(function(card){\n        var txt = card.textContent.toLowerCase();\n        \n        if (txt.includes('ransomware france') || txt.includes('ransomware fr')) {\n          card.remove();\n          return;\n        }\n\n        if (txt.includes('cnil') && !card.querySelector('.ss-flag-fr')) {\n          var heading = card.querySelector('h3, h4, .ss-card-title, strong, a');\n          if (heading) {\n            heading.insertAdjacentHTML('beforeend', `\n              <svg class=\"ss-flag-fr\" width=\"14\" height=\"10\" viewBox=\"0 0 3 2\">\n                <rect width=\"1\" height=\"2\" fill=\"#002395\"\/>\n                <rect width=\"1\" height=\"2\" x=\"1\" fill=\"#fff\"\/>\n                <rect width=\"1\" height=\"2\" x=\"2\" fill=\"#ED2939\"\/>\n              <\/svg>\n            `);\n          }\n        }\n      });\n    }\n\n    var bars = document.querySelectorAll('.ss-chart-bar, .ss-bar, [data-value], div[style*=\"height\"]');\n    bars.forEach(function(bar){\n      var val = parseInt(bar.getAttribute('data-value') || bar.innerText || '0', 10);\n      if(isNaN(val) || val <= 0) return;\n      \n      if(!bar.classList.contains('ss-val-added')){\n        bar.classList.add('ss-val-added');\n        var label = document.createElement('span');\n        label.className = 'ss-bar-value';\n        label.textContent = val;\n        if(bar.parentNode && !bar.parentNode.querySelector('.ss-bar-value')){\n          bar.parentNode.insertBefore(label, bar);\n        }\n      }\n    });\n  });\n\n  observer.observe(document.body, { childList: true, subtree: true });\n});\n<\/script>\n\n<button id=\"ss-sidebar-toggle\" class=\"ss-sidebar-toggle-btn\">\u2630 Menu Signal<\/button>\n\n<div style=\"display:flex;flex-wrap:wrap;max-width:1400px;margin:0 auto;\">\n\n  <nav id=\"ss-sidebar\" style=\"width:220px;flex:0 0 220px;padding:28px 16px 28px 24px;box-sizing:border-box;position:sticky;top:20px;align-self:flex-start;\">\n    <div style=\"font-size:10px;font-weight:700;letter-spacing:0.08em;color:#8a8a9a;text-transform:uppercase;margin:0 0 8px;\">Monitoring<\/div>\n    <a href=\"https:\/\/www.stroople.com\/signal-ransomware\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Ransomware<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-vulnerabilities-kev\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Vulnerabilities<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-data-breaches\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Data Breaches<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-cnil-sanctions-france\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">CNIL Sanctions<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-phishing\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Phishing<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-malware-infrastructure\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Malware Infrastructure<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-threat-actors\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Threat Actors<\/a>\n\n    <div style=\"font-size:10px;font-weight:700;letter-spacing:0.08em;color:#8a8a9a;text-transform:uppercase;margin:20px 0 8px;\">Tools<\/div>\n    <a href=\"https:\/\/www.stroople.com\/signal-my-exposure\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">My Exposure<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-third-parties\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Third Parties<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-fingerprint\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Fingerprinting<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-link-exposure\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Link Exposure<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-password-checker\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Password Checker<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-ip-url-check\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">IP\/URL Check<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-cyber-index\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">Cyber Index<\/a>\n    <a href=\"https:\/\/www.stroople.com\/signal-my-alerts\/\" onmouseover=\"this.style.background='#EDEAFF';this.style.color='#09025F';\" onmouseout=\"this.style.background='transparent';this.style.color='#3a3a4a';\" style=\"display:flex;align-items:center;gap:8px;font-size:14px;color:#3a3a4a;text-decoration:none;padding:8px 10px;border-radius:6px;margin-bottom:2px;transition:background .15s,color .15s;\">My Alerts<\/a>\n  <\/nav>\n\n  <div style=\"flex:1 1 400px;min-width:0;\">\n    <div style=\"max-width:1160px;margin:0 auto;padding:0 24px 40px;box-sizing:border-box;\">\n\n      <!-- WAR ROOM -->\n      <div id=\"ss-hub-crisisroom\" style=\"margin-top:20px;\"><div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Loading War Room\u2026<\/div><\/div>\n\n      <!-- TEXTE D'INTRODUCTION -->\n      <p style=\"color:#5a5a6e;font-size:14px;line-height:1.5;margin:24px 0 0;text-align:justify;\">\n        Continuous cyber threat monitoring, aggregated automatically from public sources. Ransomware, actively exploited vulnerabilities, data breaches and CNIL sanctions, all in one place.\n      <\/p>\n\n      <!-- THREAT CONSTELLATION -->\n      <div class=\"ss-hub-constellation-card\">\n        <div class=\"ss-hub-constellation-header\">\n          <span class=\"ss-hub-constellation-title\">Threat constellation \u2014 Top 20<\/span>\n          <span class=\"ss-hub-constellation-sub\" id=\"ss-hub-constellation-sub\"><\/span>\n        <\/div>\n        <div id=\"ss-hub-constellation-legend\" class=\"ss-hub-constellation-legend\"><\/div>\n        <div id=\"ss-hub-constellation-area\"><div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Loading constellation\u2026<\/div><\/div>\n        <div style=\"font-size:11px;color:#c0c0cc;font-family:Montserrat,sans-serif;margin-top:10px;\">The most active ransomware groups worldwide, linked to their known tools, MITRE ATT&amp;CK tactics and exploited vulnerabilities. Drag a node, hover for detail, or click a group to pin its connections and explore them one by one.<\/div>\n        <div style=\"font-size:10px;color:#c0c0cc;font-family:Montserrat,sans-serif;margin-top:4px;\">Only the top 10 tools, tactics and vulnerabilities per group are shown.<\/div>\n      <\/div>\n\n      <!-- INDICATORS -->\n      <div style=\"display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:8px;margin:36px 0 4px;\">\n        <h2 style=\"color:#09025F;font-size:20px;margin:0;font-weight:700;\">Indicators \u2014 last 7 days<\/h2>\n      <\/div>\n      <div id=\"ss-hub-legend\" style=\"font-size:11px;color:#8a8a9a;display:flex;flex-wrap:wrap;gap:6px 18px;margin:0 0 16px;\"><\/div>\n      <div id=\"ss-hub-indicators\" style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;\"><div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Loading indicators\u2026<\/div><\/div>\n\n      <!-- LATEST EVENTS -->\n      <div style=\"display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:8px;margin:36px 0 4px;\">\n        <h2 style=\"color:#09025F;font-size:20px;margin:0;font-weight:700;\">Latest events<\/h2>\n        <span style=\"font-size:11px;color:#8a8a9a;\">3 most recent per category<\/span>\n      <\/div>\n      <div id=\"ss-hub-recent\" style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;\"><div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Loading latest events\u2026<\/div><\/div>\n\n      <!-- CTA -->\n      <div style=\"margin:40px 0 0;background:#09025F;border-radius:14px;padding:26px 30px;display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;color:#fff;\">\n        <div>\n          <h3 style=\"font-size:16px;margin:0 0 4px;\">Is your organisation exposed?<\/h3>\n          <p style=\"font-size:13px;color:#b9b7de;margin:0;\">Contact our experts for a tailored assessment of your attack surface and cyber exposure.<\/p>\n        <\/div>\n        <a href=\"https:\/\/www.stroople.com\/contact-stroople-cybersecurity\/\" style=\"background:#00A7C4;color:#04342C;font-size:13px;font-weight:700;padding:11px 22px;border-radius:8px;text-decoration:none;white-space:nowrap;\">Contact us<\/a>\n      <\/div>\n\n      <div style=\"text-align:center;font-size:11px;color:#a0a0b0;margin:32px 0 0;\">\n        Data is aggregated automatically from public sources. For informational purposes only.\n      <\/div>\n\n    <\/div>\n  <\/div>\n\n<\/div>\n<\/div>\n\n<script>\n(function(){\n  var btn=document.getElementById('ss-sidebar-toggle');\n  var sidebar=document.getElementById('ss-sidebar');\n  if(btn&&sidebar){\n    btn.addEventListener('click', function(){\n      sidebar.classList.toggle('ss-open');\n    });\n  }\n})();\n<\/script>\n\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-858f2aa syd-sticky-section-no elementor-widget elementor-widget-html\" data-id=\"858f2aa\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<script>\nwindow.SSHub = window.SSHub || {};\n\n(function(SSHub){\n\n  function esc(str){var d=document.createElement('div');d.textContent=str||'';return d.innerHTML;}\n\n  function fmtDate(iso){\n    if(!iso) return '';\n    var p = iso.slice(0,10).split('-');\n    if(p.length<3) return iso;\n    var d = new Date(parseInt(p[0],10), parseInt(p[1],10)-1, parseInt(p[2],10));\n    if(isNaN(d)) return iso;\n    return d.toLocaleDateString('en-GB', { day:'numeric', month:'short' });\n  }\n\n  function fmtEuro(amount){\n    if(!amount) return '';\n    if(amount >= 1000000) return (amount\/1000000).toFixed(2)+' M\u20ac';\n    if(amount >= 1000) return Math.round(amount\/1000)+' K\u20ac';\n    return amount+' \u20ac';\n  }\n\n  \/\/ Threat Actors utilise un timestamp Unix pour \"date\" (comme le\n  \/\/ ticker CERT-FR), contrairement aux autres sources ci-dessus qui stockent\n  \/\/ deja une chaine \"YYYY-MM-DD\".\n  function fmtDateFromTs(ts){\n    if(!ts) return '';\n    var d = new Date(ts*1000);\n    if(isNaN(d)) return '';\n    return d.toLocaleDateString('en-GB', { day:'numeric', month:'short' });\n  }\n\n  \/* ---------------------------------------------------------------\n     Day-bucketing helpers\n  --------------------------------------------------------------- *\/\n  function dayKey(d){ return d.toISOString().slice(0,10); }\n\n  function last7Labels(){\n    var days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];\n    var out = [];\n    var today = new Date();\n    for(var i=6;i>=0;i--){\n      var d = new Date(today); d.setDate(d.getDate()-i);\n      out.push({ key: dayKey(d), label: i===0 ? 'Today' : days[d.getDay()] });\n    }\n    return out;\n  }\n\n  function bucket7and30(entries, dateField){\n    var today = new Date();\n    var cutoff7 = new Date(today); cutoff7.setDate(cutoff7.getDate()-6);\n    var cutoff30 = new Date(today); cutoff30.setDate(cutoff30.getDate()-29);\n\n    var byDay = {};\n    var count7 = 0, count30 = 0;\n    (entries||[]).forEach(function(e){\n      var raw = e[dateField];\n      if(!raw) return;\n      var d = new Date(raw.slice(0,10)+'T00:00:00Z');\n      if(isNaN(d)) return;\n      if(d >= cutoff30 && d <= today) count30++;\n      if(d >= cutoff7 && d <= today){\n        count7++;\n        var k = dayKey(d);\n        byDay[k] = (byDay[k]||0)+1;\n      }\n    });\n\n    var labels = last7Labels();\n    var counts = labels.map(function(l){ return byDay[l.key]||0; });\n    var avg30 = count30 \/ 30;\n\n    return { labels: labels, counts: counts, total7d: count7, avg30d: avg30 };\n  }\n\n  function pctVsAvg(total7d, avg30d){\n    var expected7 = avg30d * 7;\n    if(expected7 <= 0) return total7d > 0 ? '+\u221e%' : null;\n    var pct = Math.round(((total7d - expected7) \/ expected7) * 100);\n    if(pct === 0) return null;\n    return (pct > 0 ? '+' : '') + pct + '%';\n  }\n\n  \/* ---------------------------------------------------------------\n     Mini bar chart\n  --------------------------------------------------------------- *\/\n  function miniBarChart(bucket, color, todayColor){\n    var w = 240, h = 66, padB = 14, padT = 14;\n    var maxV = Math.max.apply(null, bucket.counts.concat([bucket.avg30d, 1]));\n    var barW = (w \/ bucket.counts.length) * 0.55;\n    var gap = (w \/ bucket.counts.length);\n\n    function y(v){ return padT + (h-padT-padB) * (1 - (v\/maxV)); }\n\n    var bars = '', values = '';\n    bucket.counts.forEach(function(v,i){\n      var x = i*gap + (gap-barW)\/2;\n      var barH = (h-padT-padB) * (v\/maxV);\n      var barTop = h-padB-barH;\n      var isToday = i === bucket.counts.length-1;\n      bars += '<rect x=\"'+x+'\" y=\"'+barTop+'\" width=\"'+barW+'\" height=\"'+Math.max(barH,1)+'\" rx=\"2\" fill=\"'+(isToday?todayColor:color)+'\"><title>'+bucket.labels[i].label+': '+v+'<\/title><\/rect>';\n      if(v > 0){\n        values += '<text x=\"'+(x+barW\/2)+'\" y=\"'+(barTop-3)+'\" text-anchor=\"middle\" font-size=\"9\" font-weight=\"700\" fill=\"#09025F\">'+v+'<\/text>';\n      }\n    });\n\n    var labels = bucket.counts.map(function(v,i){\n      var x = i*gap + gap\/2;\n      return '<text x=\"'+x+'\" y=\"'+h+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"#b0b0bc\">'+esc(bucket.labels[i].label==='Today'?'TDY':bucket.labels[i].label.slice(0,1))+'<\/text>';\n    }).join('');\n\n    var avgY = y(bucket.avg30d);\n    var avgLine = '<line x1=\"0\" y1=\"'+avgY+'\" x2=\"'+w+'\" y2=\"'+avgY+'\" stroke=\"#b0b0bc\" stroke-width=\"1\" stroke-dasharray=\"3,3\"\/>';\n\n    return '<svg viewBox=\"0 0 '+w+' '+h+'\" width=\"100%\" height=\"'+h+'\" preserveAspectRatio=\"none\">'+avgLine+bars+values+labels+'<\/svg>';\n  }\n\n  \/* ---------------------------------------------------------------\n     Threat index\n  --------------------------------------------------------------- *\/\n  var THREAT_BANDS = [\n    { max: 24,  band: 'Low',     color: '#639922', range: '0\u201324'   },\n    { max: 49,  band: 'Moderate', color: '#EF9F27', range: '25\u201349'  },\n    { max: 74,  band: 'High',     color: '#D85A30', range: '50\u201374'  },\n    { max: 100, band: 'Critical', color: '#E24B4A', range: '75\u2013100' }\n  ];\n\n  function computeThreatIndex(frRw7d, worldRw7d, worldRwAvg30, kevCritical7d, breach7d, malwareRw7d, malwareAvg30, phishRw7d, phishAvg30){\n    var worldSpike = spikeRatio(worldRw7d, worldRwAvg30);\n    var malwareSpike = spikeRatio(malwareRw7d, malwareAvg30);\n    var phishSpike = spikeRatio(phishRw7d, phishAvg30);\n    var avgSpike = (malwareSpike + phishSpike) \/ 2;\n\n    var pts = 0;\n    pts += Math.min(frRw7d * 12, 20);                              \/\/ sature vers 1,7 victime FR\n    pts += Math.min(Math.max(worldSpike-1,0) * 40, 30);             \/\/ sature a +75% vs moyenne 30j\n    pts += Math.min(kevCritical7d * 10, 20);                        \/\/ sature a 2 CVE critiques\n    pts += Math.min(breach7d * 1.5, 15);                            \/\/ sature vers 10 fuites FR\n    pts += Math.min(Math.max(avgSpike-1,0) * 20, 15);               \/\/ sature a +75% vs moyenne 30j\n    var score = Math.max(0, Math.min(100, Math.round(pts)));\n\n    var match = THREAT_BANDS.find(function(b){ return score <= b.max; }) || THREAT_BANDS[THREAT_BANDS.length-1];\n\n    return { score: score, band: match.band, color: match.color };\n  }\n\n  \/\/ Ratio d'activite de la semaine par rapport a la moyenne 30 jours\n  \/\/ projetee sur 7 jours. > 1 = au dessus de la normale. Reutilise pour\n  \/\/ le ransomware monde, ThreatFox et PhishStats.\n  function spikeRatio(total7d, avg30d){\n    return avg30d > 0 ? (total7d\/(avg30d*7)) : (total7d>0 ? 2 : 0);\n  }\n\n  function threatLegendHtml(){\n    return '<div style=\"display:flex;flex-wrap:wrap;gap:14px;margin-top:12px;font-size:11px;color:#8a8578;\">'\n      + THREAT_BANDS.map(function(b){\n          return '<span style=\"display:inline-flex;align-items:center;gap:5px;\"><span style=\"width:8px;height:8px;border-radius:50%;background:'+b.color+';display:inline-block;\"><\/span>'+b.band+' ('+b.range+')<\/span>';\n        }).join('')\n      + '<\/div>';\n  }\n\n  \/* ---------------------------------------------------------------\n     Clock (Europe\/Paris)\n  --------------------------------------------------------------- *\/\n  function startClock(){\n    var timeEl = document.getElementById('ss-cr-time');\n    var dateEl = document.getElementById('ss-cr-date');\n    if(!timeEl || !dateEl) return;\n    function tick(){\n      var now = new Date();\n      timeEl.textContent = now.toLocaleTimeString('en-GB', { hour:'2-digit', minute:'2-digit', second:'2-digit', timeZone:'Europe\/Paris' });\n      dateEl.textContent = now.toLocaleDateString('en-GB', { weekday:'long', day:'numeric', month:'long', timeZone:'Europe\/Paris' });\n    }\n    tick();\n    setInterval(tick, 1000);\n  }\n\n  \/* ---------------------------------------------------------------\n     Main render\n  --------------------------------------------------------------- *\/\n  SSHub.render = function(payload){\n    var rw = payload.ransomware || {};\n    var rwArchive = (payload.ransomware_archive || {}).entries || [];\n    var kevFull = (payload.kev_full || {}).entries || [];\n    var fuites = payload.fuites || {};\n    var cnil = payload.cnil || {};\n    var cnilAll = cnil.sanctions_all || [];\n    var phishArchive = (payload.phishstats_archive || {}).entries || [];\n    var threatfoxArchive = (payload.threatfox_archive || {}).entries || [];\n    var ctiArchive = (payload.cti_intl_archive || {}).entries || [];\n    var epssWatch = (payload.epss_watchlist || {}).entries || [];\n\n    var frBucket = bucket7and30(rw.fr_7d || [], 'date');\n    frBucket.total7d = rw.fr_7d_count || frBucket.total7d;\n\n    var worldBucket = bucket7and30(rwArchive, 'date');\n\n    var kevBucket = bucket7and30(kevFull, 'added');\n    var kevCritical7d = (kevFull||[]).filter(function(e){\n      if(!e.added) return false;\n      var d = new Date(e.added.slice(0,10)+'T00:00:00Z');\n      var cutoff = new Date(); cutoff.setDate(cutoff.getDate()-6);\n      if(d < cutoff) return false;\n      return e.severity ? e.severity === 'CRITICAL' : e.ransomware;\n    }).length;\n\n    var breachBucket = bucket7and30(fuites.fr_recent || [], 'date');\n    breachBucket.total7d = fuites.fr_7d_count || breachBucket.total7d;\n\n    var cnilBucket = bucket7and30(cnilAll, 'date');\n\n    var phishBucket = bucket7and30(phishArchive, 'date');\n    phishBucket.total7d = (payload.phishstats || {}).new_7d || phishBucket.total7d;\n\n    var malwareBucket = bucket7and30(threatfoxArchive, 'first_seen');\n    malwareBucket.total7d = (payload.threatfox || {}).new_7d || malwareBucket.total7d;\n\n    var epssCount = epssWatch.length;\n    var epssAvgScore = epssCount ? Math.round(epssWatch.reduce(function(s,e){ return s+(e.epss||0); },0)\/epssCount*100) : 0;\n\n    \/\/ Rapports Threat Actors : \"date\" y est un timestamp Unix (comme\n    \/\/ pour le ticker CERT-FR), pas une chaine \"YYYY-MM-DD\" comme les sources\n    \/\/ ci-dessus : on convertit juste ce qu'on passe a bucket7and30(), sans\n    \/\/ toucher a son contrat existant.\n    var ctiBucket = bucket7and30(ctiArchive.map(function(e){\n      return { date: e.date ? new Date(e.date*1000).toISOString().slice(0,10) : null };\n    }), 'date');\n    var ctiMitreCount7d = (function(){\n      var cutoff = new Date(); cutoff.setDate(cutoff.getDate()-6);\n      return ctiArchive.filter(function(e){\n        if(!e.date || !e.group || !e.group.id) return false;\n        return new Date(e.date*1000) >= cutoff;\n      }).length;\n    })();\n\n    \/\/ Vendor Advisories (v4.93) : meme logique que ctiMitreCount7d\n    \/\/ ci-dessus, appliquee a payload.psirt_hub (liste d'entrees {date,\n    \/\/ kev} sur 35 jours, voir ss_psirt_hub_entries() cote plugin).\n    var psirtEntries = payload.psirt_hub || [];\n    var psirtBucket = bucket7and30(psirtEntries, 'date');\n    var psirtKev7d = (function(){\n      var cutoff = new Date(); cutoff.setDate(cutoff.getDate()-6);\n      return psirtEntries.filter(function(e){\n        if(!e.date || !e.kev) return false;\n        var d = new Date(e.date.slice(0,10)+'T00:00:00Z');\n        return d >= cutoff;\n      }).length;\n    })();\n\n    var threat = computeThreatIndex(frBucket.total7d, worldBucket.total7d, worldBucket.avg30d, kevCritical7d, breachBucket.total7d, malwareBucket.total7d, malwareBucket.avg30d, phishBucket.total7d, phishBucket.avg30d);\n\n    renderWarRoom(threat, frBucket, worldBucket);\n    renderLegend();\n    renderIndicatorCards(kevBucket, worldBucket, breachBucket, cnilBucket, phishBucket, malwareBucket, epssCount, epssAvgScore, ctiBucket, ctiMitreCount7d, psirtBucket, psirtKev7d);\n    renderRecentEvents(rwArchive, kevFull, fuites, cnilAll, phishArchive, threatfoxArchive, ctiArchive);\n    renderConstellation((payload.ransomware_top_groups || {}).groups || [], kevFull);\n    startClock();\n  };\n\n  \/* ---------------------------------------------------------------\n     War Room\n  --------------------------------------------------------------- *\/\n  function renderWarRoom(threat, frBucket, worldBucket){\n    var el = document.getElementById('ss-hub-crisisroom');\n    if(!el) return;\n\n    var headline = worldBucket.total7d > 0\n      ? 'Sustained ransomware activity: ' + worldBucket.total7d + ' victims worldwide'\n      : 'No significant ransomware activity in the last 7 days';\n\n    var subline = frBucket.total7d > 0\n      ? frBucket.total7d + ' French target' + (frBucket.total7d>1?'s':'') + ' recorded this week'\n      : 'No French targets recorded at this time';\n\n    var trend = pctVsAvg(worldBucket.total7d, worldBucket.avg30d);\n\n    el.innerHTML =\n      '<div style=\"background:linear-gradient(135deg,#FFF6EE 0%,#FFEFE0 100%);border:1px solid #F5D9BC;border-radius:16px;padding:24px 28px;\">'\n      + '<div style=\"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:18px;\">'\n        + '<div style=\"display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;letter-spacing:0.06em;color:#854F0B;text-transform:uppercase;\"><span style=\"width:8px;height:8px;border-radius:50%;background:'+threat.color+';display:inline-block;\"><\/span>War room<\/div>'\n        + '<div style=\"font-size:11px;font-weight:700;letter-spacing:0.04em;color:#a08a72;text-transform:uppercase;\">Last 7 days<\/div>'\n      + '<\/div>'\n      + '<div style=\"display:flex;flex-wrap:wrap;gap:24px;align-items:flex-start;\">'\n        + '<div style=\"display:flex;gap:18px;flex:1 1 280px;\">'\n          + '<div style=\"width:52px;height:52px;border-radius:14px;background:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:0 1px 4px rgba(0,0,0,0.06);\"><svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+threat.color+'\" stroke-width=\"2\"><path d=\"M12 3l8 4v5c0 4.5-3.2 7.9-8 9-4.8-1.1-8-4.5-8-9V7z\"\/><path d=\"M12 8v5M12 16h.01\"\/><\/svg><\/div>'\n          + '<div style=\"min-width:0;\">'\n            + '<div style=\"display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px;\">'\n              + '<span style=\"font-size:32px;font-weight:800;line-height:1;color:'+threat.color+';\">'+threat.band+'<\/span>'\n              + '<span style=\"font-size:13px;font-weight:700;padding:5px 12px;border-radius:20px;border:1px solid '+threat.color+';color:'+threat.color+';\">'+threat.score+'\/100<\/span>'\n              + (trend?'<span style=\"font-size:13px;font-weight:700;color:'+threat.color+';\">'+trend+' vs 30-day average<\/span>':'')\n            + '<\/div>'\n            + '<p style=\"font-size:16px;font-weight:700;color:#2c2c2a;margin:0 0 4px;\">'+esc(headline)+'<\/p>'\n            + '<p style=\"font-size:13px;color:#8a8578;margin:0;\">'+esc(subline)+'<\/p>'\n            + '<div style=\"display:flex;gap:26px;margin-top:18px;flex-wrap:wrap;\">'\n              + counter(frBucket.total7d, 'France', '#E24B4A')\n              + counter(worldBucket.total7d, 'World', '#2c2c2a')\n            + '<\/div>'\n          + '<\/div>'\n        + '<\/div>'\n        + '<div style=\"text-align:left;flex:0 0 180px;padding-left:24px;border-left:1px solid #F5D9BC;\">'\n          + '<div style=\"display:flex;align-items:center;gap:5px;font-size:11px;font-weight:700;color:#a08a72;text-transform:uppercase;letter-spacing:0.05em;\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#a08a72\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"9\"\/><path d=\"M12 7v5l3 3\"\/><\/svg>Paris<\/div>'\n          + '<div id=\"ss-cr-time\" style=\"font-size:28px;font-weight:800;color:#2c2c2a;font-variant-numeric:tabular-nums;margin:4px 0 2px;\">--:--:--<\/div>'\n          + '<div id=\"ss-cr-date\" style=\"font-size:12px;color:#8a8578;\">\u2014<\/div>'\n        + '<\/div>'\n      + '<\/div>'\n      + threatLegendHtml()\n      + '<\/div>';\n  }\n\n  function counter(value, label, color){\n    return '<div><span style=\"font-size:26px;font-weight:800;color:'+color+';\">'+value+'<\/span><span style=\"font-size:10px;font-weight:700;letter-spacing:0.05em;color:#8a8578;text-transform:uppercase;margin-left:6px;\">'+esc(label)+'<\/span><\/div>';\n  }\n\n  function renderLegend(){\n    var el = document.getElementById('ss-hub-legend');\n    if(!el) return;\n    el.innerHTML =\n      '<span>Bars = daily activity (last 7 days)<\/span>'\n      + '<span><span style=\"border-bottom:2px dashed #b0b0bc;width:14px;display:inline-block;margin-right:4px;\"><\/span>Dashed = 30-day average, scaled to a 7-day window<\/span>'\n      + '<span>Period: last 7 days<\/span>';\n  }\n\n  \/* ---------------------------------------------------------------\n     Indicator cards\n  --------------------------------------------------------------- *\/\n  var FLAG_FR_SVG = '<svg width=\"14\" height=\"10\" viewBox=\"0 0 3 2\" style=\"display:inline-block;vertical-align:middle;margin-left:6px;border-radius:2px;box-shadow:0 0 1px rgba(0,0,0,0.3);\"><rect width=\"1\" height=\"2\" fill=\"#002395\"\/><rect width=\"1\" height=\"2\" x=\"1\" fill=\"#fff\"\/><rect width=\"1\" height=\"2\" x=\"2\" fill=\"#ED2939\"\/><\/svg>';\n\n  function renderIndicatorCards(kevBucket, worldBucket, breachBucket, cnilBucket, phishBucket, malwareBucket, epssCount, epssAvgScore, ctiBucket, ctiMitreCount7d, psirtBucket, psirtKev7d){\n    var el = document.getElementById('ss-hub-indicators');\n    if(!el) return;\n\n    var cards = [\n      {\n        title: 'Vulnerabilities (KEV)', icon: iconBug('#185FA5'), bg:'#E6F1FB', border:'#185FA5', color:'#185FA5', today:'#0C447C',\n        value: kevBucket.total7d,\n        desc: kevBucket.total7d+' new vulnerabilit'+(kevBucket.total7d===1?'y':'ies')+' added to the CISA KEV catalog',\n        extra: epssCount ? epssCount+' emerging risk CVE'+(epssCount===1?'':'s')+' tracked (EPSS), avg '+epssAvgScore+'%' : null,\n        bucket: kevBucket, url: 'https:\/\/www.stroople.com\/signal-vulnerabilities-kev\/'\n      },\n      {\n        title: 'Vendor Advisories', icon: iconBulletin('#475569'), bg:'#F1F5F9', border:'#475569', color:'#475569', today:'#1E293B',\n        value: psirtBucket.total7d,\n        desc: psirtBucket.total7d+' new vendor advisor'+(psirtBucket.total7d===1?'y':'ies')+' published this week',\n        extra: psirtKev7d ? psirtKev7d+' carrying an actively exploited CVE' : null,\n        bucket: psirtBucket, url: 'https:\/\/www.stroople.com\/signal-vulnerabilities\/'\n      },\n      {\n        title: 'Ransomware world', icon: iconShield('#EF9F27'), bg:'#FAEEDA', border:'#EF9F27', color:'#EF9F27', today:'#854F0B',\n        value: worldBucket.total7d,\n        desc: (function(){ var t=pctVsAvg(worldBucket.total7d, worldBucket.avg30d); return t? 'Activity '+(parseInt(t,10)>0?'up':'down')+': '+t+' vs average' : 'Activity in line with the 30-day average'; })(),\n        bucket: worldBucket, url: 'https:\/\/www.stroople.com\/signal-ransomware\/'\n      },\n      {\n        title: 'Data breaches', icon: iconDb('#7f77dd'), bg:'#EEEDFE', border:'#7f77dd', color:'#7f77dd', today:'#534AB7',\n        value: breachBucket.total7d,\n        desc: breachBucket.total7d+' breach'+(breachBucket.total7d===1?'':'es')+' recorded over 7 days',\n        bucket: breachBucket, url: 'https:\/\/www.stroople.com\/signal-data-breaches\/'\n      },\n      {\n        title: 'CNIL sanctions', flag: FLAG_FR_SVG, icon: iconGavel('#EF9F27'), bg:'#FAEEDA', border:'#EF9F27', color:'#EF9F27', today:'#854F0B',\n        value: cnilBucket.total7d,\n        desc: cnilBucket.total7d>0 ? cnilBucket.total7d+' new sanction'+(cnilBucket.total7d>1?'s':'')+' this week' : 'No new sanctions this week',\n        bucket: cnilBucket, url: 'https:\/\/www.stroople.com\/signal-cnil-sanctions-france\/'\n      },\n      {\n        title: 'Phishing', icon: iconHook('#0E7C86'), bg:'#E3F4F6', border:'#0E7C86', color:'#0E7C86', today:'#0B5960',\n        value: phishBucket.total7d,\n        desc: phishBucket.total7d+' new phishing URL'+(phishBucket.total7d===1?'':'s')+' detected this week',\n        bucket: phishBucket, url: 'https:\/\/www.stroople.com\/signal-phishing\/'\n      },\n      {\n        title: 'Malware Infrastructure', icon: iconServer('#C2410C'), bg:'#FDECE3', border:'#C2410C', color:'#C2410C', today:'#7C2D12',\n        value: malwareBucket.total7d,\n        desc: malwareBucket.total7d+' new indicator'+(malwareBucket.total7d===1?'':'s')+' of compromise this week',\n        bucket: malwareBucket, url: 'https:\/\/www.stroople.com\/signal-malware-infrastructure\/'\n      },\n      {\n        title: 'Threat Actors', icon: iconGlobe('#BE185D'), bg:'#FCE7F3', border:'#BE185D', color:'#BE185D', today:'#831843',\n        value: ctiBucket.total7d,\n        desc: ctiBucket.total7d+' new report'+(ctiBucket.total7d===1?'':'s')+' from national CERT\/CSIRT agencies this week',\n        extra: ctiMitreCount7d ? ctiMitreCount7d+' linked to a known MITRE ATT&CK actor' : null,\n        bucket: ctiBucket, url: 'https:\/\/www.stroople.com\/signal-threat-actors\/'\n      }\n    ];\n\n    el.innerHTML = cards.map(function(c){\n      return '<a href=\"'+c.url+'\" style=\"background:#fff;border-radius:12px;border:1px solid #ececf2;border-top:3px solid '+c.border+';padding:18px 18px 16px;text-decoration:none;display:block;color:inherit;\">'\n        + '<div style=\"display:flex;align-items:center;gap:10px;margin-bottom:14px;\">'\n          + '<div style=\"width:32px;height:32px;border-radius:9px;background:'+c.bg+';display:flex;align-items:center;justify-content:center;flex-shrink:0;\">'+c.icon+'<\/div>'\n          + '<div><div style=\"font-size:12px;font-weight:700;color:#09025F;text-transform:uppercase;letter-spacing:0.03em;\">'+esc(c.title)+(c.flag||'')+'<\/div><div style=\"font-size:10px;color:#a0a0b0;margin-top:1px;\">Last 7 days<\/div><\/div>'\n        + '<\/div>'\n        + '<div><span style=\"font-size:28px;font-weight:800;color:'+c.color+';\">'+c.value+'<\/span><span style=\"font-size:11px;color:#a0a0b0;font-weight:600;margin-left:6px;\">avg '+c.bucket.avg30d.toFixed(1)+'\/7d<\/span><\/div>'\n        + '<div style=\"font-size:12px;color:#5a5a6e;line-height:1.5;margin:4px 0 12px;min-height:32px;\">'+esc(c.desc)+'<\/div>'\n        + (c.extra ? '<div style=\"display:inline-flex;align-items:center;font-size:10px;font-weight:700;background:#fee2e2;color:#991b1b;padding:3px 9px;border-radius:20px;margin:-6px 0 10px;\">'+esc(c.extra)+'<\/div>' : '')\n        + miniBarChart(c.bucket, c.color, c.today)\n      + '<\/a>';\n    }).join('');\n  }\n\n  \/* ---------------------------------------------------------------\n     Latest events\n  --------------------------------------------------------------- *\/\n  function renderRecentEvents(rwArchive, kevFull, fuites, cnilAll, phishArchive, threatfoxArchive, ctiArchive){\n    var el = document.getElementById('ss-hub-recent');\n    if(!el) return;\n\n    var rw3 = rwArchive.slice().sort(function(a,b){ return (b.ts||0)-(a.ts||0); }).slice(0,3);\n    var kev3 = kevFull.slice().sort(function(a,b){ return (b.added||'').localeCompare(a.added||''); }).slice(0,3);\n    var br3 = (fuites.fr_recent||[]).slice().sort(function(a,b){ return (b.ts||0)-(a.ts||0); }).slice(0,3);\n    var cnil3 = cnilAll.slice().sort(function(a,b){ return (b.date||'').localeCompare(a.date||''); }).slice(0,3);\n    var phish3 = phishArchive.slice().sort(function(a,b){ return (b.date||'').localeCompare(a.date||''); }).slice(0,3);\n    var malware3 = threatfoxArchive.slice().sort(function(a,b){ return (b.first_seen||'').localeCompare(a.first_seen||''); }).slice(0,3);\n    \/\/ \"date\" est un timestamp Unix ici (voir la remarque dans SSHub.render) :\n    \/\/ comparaison numerique directe, pas de localeCompare sur une chaine.\n    var cti3 = (ctiArchive||[]).slice().sort(function(a,b){ return (b.date||0)-(a.date||0); }).slice(0,3);\n\n    var cols = [\n      {\n        title: 'Ransomware', dot: '#D85A30', url: 'https:\/\/www.stroople.com\/signal-ransomware\/',\n        items: rw3.map(function(e){ return { name: e.victim||'Unknown', meta: fmtDate(e.date)+(e.country?' \u00b7 '+e.country:''), tag: e.group||'' }; })\n      },\n      {\n        title: 'Vulnerabilities', dot: '#185FA5', url: 'https:\/\/www.stroople.com\/signal-vulnerabilities-kev\/',\n        items: kev3.map(function(e){ return { name: e.cve||'Unknown CVE', meta: [e.vendor,e.product].filter(Boolean).join(' \u2014 ')+(e.added?' \u00b7 '+fmtDate(e.added):''), tag: e.severity || (e.ransomware?'Ransomware-linked':'') }; })\n      },\n      {\n        title: 'Data breaches', dot: '#7f77dd', url: 'https:\/\/www.stroople.com\/signal-data-breaches\/',\n        items: br3.map(function(e){ return { name: e.name||'Unknown', meta: fmtDate(e.date), tag: e.confidence||'' }; })\n      },\n      {\n        title: 'CNIL sanctions', dot: '#EF9F27', url: 'https:\/\/www.stroople.com\/signal-cnil-sanctions-france\/',\n        items: cnil3.map(function(e){ return { name: e.organisme||'Unknown', meta: fmtDate(e.date)+(e.montant?' \u00b7 '+fmtEuro(e.montant):''), tag: e.gravite||'' }; })\n      },\n      {\n        title: 'Phishing', dot: '#0E7C86', url: 'https:\/\/www.stroople.com\/signal-phishing\/',\n        items: phish3.map(function(e){ return { name: e.domain||'Unknown', meta: fmtDate(e.date)+(e.countrycode?' \u00b7 '+e.countrycode:''), tag: (e.score!=null?'score '+e.score.toFixed(1):'') }; })\n      },\n      {\n        title: 'Malware Infrastructure', dot: '#C2410C', url: 'https:\/\/www.stroople.com\/signal-malware-infrastructure\/',\n        items: malware3.map(function(e){ return { name: e.ioc||'Unknown', meta: (e.malware||'Unknown malware')+(e.first_seen?' \u00b7 '+fmtDate(e.first_seen):''), tag: (e.confidence!=null?'confidence '+e.confidence+'%':'') }; })\n      },\n      {\n        title: 'Threat Actors', dot: '#BE185D', url: 'https:\/\/www.stroople.com\/signal-threat-actors\/',\n        items: cti3.map(function(e){ return { name: e.title||'Untitled report', meta: (e.source||'')+(e.date?' \u00b7 '+fmtDateFromTs(e.date):''), tag: (e.group && e.group.name) ? e.group.name : '' }; })\n      }\n    ];\n\n    el.innerHTML = cols.map(function(c){\n      var itemsHtml = c.items.length ? c.items.map(function(it){\n        return '<div style=\"padding:9px 0;border-bottom:1px solid #f0f0f5;\">'\n          + '<div style=\"font-size:13px;font-weight:600;color:#3a3a4a;\">'+esc(it.name)+'<\/div>'\n          + '<div style=\"font-size:11px;color:#8a8a9a;margin-top:2px;text-transform:capitalize;\">'+esc(it.meta)+(it.tag?' \u00b7 '+esc(it.tag):'')+'<\/div>'\n        + '<\/div>';\n      }).join('') : '<div style=\"font-size:12px;color:#a0a0b0;padding:9px 0;\">No recent events<\/div>';\n\n      return '<div style=\"background:#fff;border-radius:12px;border:1px solid #ececf2;padding:16px 18px 6px;\">'\n        + '<div style=\"display:flex;align-items:center;gap:7px;font-size:11px;font-weight:700;color:#09025F;text-transform:uppercase;letter-spacing:0.04em;margin-bottom:6px;\"><span style=\"width:7px;height:7px;border-radius:50%;background:'+c.dot+';display:inline-block;\"><\/span>'+esc(c.title)+'<\/div>'\n        + itemsHtml\n        + '<a href=\"'+c.url+'\" style=\"display:block;margin:8px 0 4px;font-size:11px;font-weight:700;color:#09025F;text-decoration:none;\">View all \u2192<\/a>'\n      + '<\/div>';\n    }).join('');\n  }\n\n  \/* ---------------------------------------------------------------\n     Threat constellation : graphe de relations (D3 force-directed)\n     entre les groupes de ransomware les plus actifs et leurs outils,\n     tactiques MITRE ATT&CK et vulnerabilites exploitees. Reutilise\n     ransomware_top_groups, deja present dans le payload section=all,\n     aucune donnee ni appel reseau supplementaire. Le clic sur un groupe\n     fige ses elements lies (outils, tactiques, CVE) pour qu'ils restent\n     cliquables individuellement tant que son encadre est ouvert ; la\n     fermeture de l'encadre relache le gel et reinitialise le graphe. La\n     vue ne renvoie plus vers aucune autre page : tout le detail utile\n     reste dans l'encadre lui-meme.\n  --------------------------------------------------------------- *\/\n  function renderConstellation(groups, kevFull){\n    var area = document.getElementById('ss-hub-constellation-area');\n    var legendEl = document.getElementById('ss-hub-constellation-legend');\n    var subEl = document.getElementById('ss-hub-constellation-sub');\n    if(!area) return;\n    if(typeof d3 === 'undefined'){ area.innerHTML = '<div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Visualization library unavailable.<\/div>'; return; }\n    if(!groups || !groups.length){ area.innerHTML = '<div style=\"padding:40px;text-align:center;color:#8a8a9a;\">No data available yet.<\/div>'; return; }\n\n    var TYPE_COLORS = {\n      group:'#09025F',\n      tool:'#00A7C4',\n      tactic:'#7C3AED',\n      cve_critical:'#991b1b',\n      cve_high:'#c2410c',\n      cve_medium:'#92400e',\n      cve_other:'#5a5a6e'\n    };\n\n    \/\/ Plafond large (la liste recue est deja triee par nombre de\n    \/\/ victimes, et limitee cote plugin a 12 groupes) : on prend tout ce\n    \/\/ qui arrive plutot que de re-couper artificiellement, la lisibilite\n    \/\/ est desormais geree par la taille des bulles et l'interaction au\n    \/\/ survol plutot que par le nombre de groupes affiches.\n    var top = groups.slice(0, 20);\n    var nodeMap = {};\n    var links = [];\n\n    \/\/ Catalogue CISA KEV deja charge sur la page hub (section=all) : sert\n    \/\/ uniquement a retrouver, sans appel reseau supplementaire, une courte\n    \/\/ description factuelle pour les CVE affichees dans la constellation.\n    var kevByCve = {};\n    (kevFull||[]).forEach(function(e){ if(e && e.cve) kevByCve[e.cve] = e; });\n\n    function getNode(id, base){\n      var n = nodeMap[id];\n      if(!n){\n        n = Object.assign({id:id, degree:0, groupNames:[]}, base);\n        nodeMap[id] = n;\n      }\n      return n;\n    }\n\n    top.forEach(function(g){\n      var gid = 'g:'+g.group;\n      var gnode = getNode(gid, {type:'group', label:g.group, altname:g.altname, victims:g.victims||0, description:g.description||'', country:g.country||null});\n      gnode.degree++;\n\n      (g.tools||[]).slice(0,10).forEach(function(t){\n        if(!t) return;\n        var tid = 'tool:'+String(t).toLowerCase();\n        var tn = getNode(tid, {type:'tool', label:t});\n        tn.degree++;\n        if(tn.groupNames.indexOf(g.group)===-1) tn.groupNames.push(g.group);\n        links.push({source:gid, target:tid, kind:'tool'});\n      });\n\n      (g.ttps||[]).slice(0,10).forEach(function(t){\n        if(!t||!t.tactic) return;\n        var tacId = 'tactic:'+String(t.tactic_id||t.tactic).toLowerCase();\n        var tn = getNode(tacId, {type:'tactic', label:t.tactic, tacticId:t.tactic_id||''});\n        tn.degree++;\n        if(tn.groupNames.indexOf(g.group)===-1) tn.groupNames.push(g.group);\n        links.push({source:gid, target:tacId, kind:'tactic'});\n      });\n\n      \/\/ CVE les plus critiques d'abord (CVSS decroissant), 10 par groupe.\n      var vulns = (g.vulnerabilities||[]).slice().sort(function(a,b){\n        return (parseFloat(b.cvss)||-1) - (parseFloat(a.cvss)||-1);\n      }).slice(0,10);\n      vulns.forEach(function(v){\n        if(!v||!v.cve) return;\n        var vid = 'cve:'+v.cve;\n        var vn = getNode(vid, {type:'cve', label:v.cve, cvss:v.cvss||null, severity:v.severity||'', vendor:v.vendor||'', product:v.product||''});\n        var kevEntry = kevByCve[v.cve];\n        if(kevEntry && kevEntry.desc) vn.kevDesc = kevEntry.desc;\n        vn.degree++;\n        if(vn.groupNames.indexOf(g.group)===-1) vn.groupNames.push(g.group);\n        links.push({source:gid, target:vid, kind:'cve'});\n      });\n    });\n\n    var nodes = Object.keys(nodeMap).map(function(k){ return nodeMap[k]; });\n    if(!nodes.length){ area.innerHTML = '<div style=\"padding:40px;text-align:center;color:#8a8a9a;\">No data available yet.<\/div>'; return; }\n\n    if(subEl) subEl.textContent = top.length+' groups \u00b7 '+nodes.length+' linked elements';\n\n    if(legendEl){\n      var legendItems = [\n        ['Ransomware group', TYPE_COLORS.group],\n        ['Tool', TYPE_COLORS.tool],\n        ['MITRE ATT&CK tactic', TYPE_COLORS.tactic],\n        ['Exploited CVE', TYPE_COLORS.cve_high]\n      ];\n      legendEl.innerHTML = legendItems.map(function(it){\n        return '<span class=\"ss-hub-constellation-legend-item\"><span class=\"ss-hub-constellation-legend-dot\" style=\"background:'+it[1]+';\"><\/span>'+esc(it[0])+'<\/span>';\n      }).join('');\n    }\n\n    function nodeColor(n){\n      if(n.type==='group') return TYPE_COLORS.group;\n      if(n.type==='tool') return TYPE_COLORS.tool;\n      if(n.type==='tactic') return TYPE_COLORS.tactic;\n      var sev = (n.severity||'').toLowerCase();\n      if(sev==='critical') return TYPE_COLORS.cve_critical;\n      if(sev==='high') return TYPE_COLORS.cve_high;\n      if(sev==='medium') return TYPE_COLORS.cve_medium;\n      return TYPE_COLORS.cve_other;\n    }\n\n    \/\/ Bulles plus petites qu'a l'origine (max reduit, echelle adoucie) pour\n    \/\/ laisser tenir davantage de groupes sans surcharger le graphe.\n    function nodeRadius(n){\n      if(n.type==='group') return Math.max(9, Math.min(24, 7+Math.sqrt(n.victims||0)*0.7));\n      return Math.max(4, Math.min(11, 4+n.degree*0.9));\n    }\n\n    area.innerHTML = '';\n    var wrap = document.createElement('div');\n    wrap.className = 'ss-hub-constellation-svg-wrap';\n    area.appendChild(wrap);\n\n    var tooltip = document.createElement('div');\n    tooltip.className = 'ss-hub-constellation-tooltip';\n    wrap.appendChild(tooltip);\n\n    \/\/ Encadre de detail, affiche au clic (plutot qu'une redirection\n    \/\/ immediate) : reste ouvert le temps de lire, avec un lien explicite\n    \/\/ vers la page pertinente pour approfondir.\n    var detailPanel = document.createElement('div');\n    detailPanel.className = 'ss-hub-constellation-detail';\n    detailPanel.innerHTML = '<button type=\"button\" class=\"ss-hub-constellation-detail-close\" aria-label=\"Close\">\u00d7<\/button><div class=\"ss-hub-constellation-detail-content\"><\/div>';\n    wrap.appendChild(detailPanel);\n    detailPanel.querySelector('.ss-hub-constellation-detail-close').addEventListener('click', function(){ hideDetailPanel(); });\n    var detailContent = detailPanel.querySelector('.ss-hub-constellation-detail-content');\n\n    \/\/ Hauteur relevee de 560 a 760 (davantage de bulles depuis le passage\n    \/\/ des plafonds par groupe a 10, certaines sortaient du cadre visible).\n    var W = Math.max(320, wrap.clientWidth||1040), H = 760;\n\n    var svg = d3.select(wrap).append('svg').attr('viewBox','0 0 '+W+' '+H).attr('preserveAspectRatio','xMidYMid meet');\n\n    var linkSel = svg.append('g').attr('stroke','#e0e0e5').attr('stroke-opacity',0.6)\n      .selectAll('line').data(links).join('line').attr('stroke-width',1.2);\n\n    var nodeSel = svg.append('g').selectAll('circle').data(nodes).join('circle')\n      .attr('class','ss-hub-constellation-node')\n      .attr('r', nodeRadius)\n      .attr('fill', nodeColor)\n      .attr('stroke','#fff')\n      .attr('stroke-width',1.5);\n\n    \/\/ Etiquettes affichees pour les 20 groupes (et non plus seulement les\n    \/\/ plus actifs) : chaque nom repose sur un petit fond clair pour rester\n    \/\/ lisible malgre les chevauchements, regroupe avec son texte dans un\n    \/\/ <g> deplace en bloc a chaque tick plutot que positionne isolement.\n    var labelGroupSel = svg.append('g').selectAll('g')\n      .data(nodes.filter(function(n){ return n.type==='group'; }))\n      .join('g').attr('class','ss-hub-constellation-labelgroup');\n\n    var labelBgSel = labelGroupSel.append('rect').attr('class','ss-hub-constellation-label-bg');\n\n    var labelSel = labelGroupSel.append('text')\n      .attr('class','ss-hub-constellation-label')\n      .attr('text-anchor','middle')\n      .attr('dy', function(n){ return -(nodeRadius(n)+6); })\n      .text(function(n){ return n.label; });\n\n    labelSel.each(function(){\n      var bbox = this.getBBox();\n      d3.select(this.parentNode).select('rect')\n        .attr('x', bbox.x-3).attr('y', bbox.y-1)\n        .attr('width', bbox.width+6).attr('height', bbox.height+2)\n        .attr('rx', 3);\n    });\n\n    function showTooltip(evt, n){\n      var html = '';\n      if(n.type==='group'){\n        html = '<strong>'+esc(n.label)+(n.altname?' ('+esc(n.altname)+')':'')+'<\/strong>'\n          + '<div class=\"ss-ctt-sub\">'+(n.victims||0).toLocaleString('en-GB')+' victims'+(n.country?' \u00b7 '+esc(n.country):'')+'<\/div>'\n          + (n.description?'<div style=\"margin-top:6px;\">'+esc(n.description.slice(0,140))+(n.description.length>140?'\u2026':'')+'<\/div>':'')\n          + '<div class=\"ss-ctt-sub\" style=\"margin-top:6px;\">Click for detail<\/div>';\n      } else if(n.type==='tool'){\n        html = '<strong>'+esc(n.label)+'<\/strong><div class=\"ss-ctt-sub\">Tool \u00b7 used by '+n.groupNames.length+' group(s)<br>'+esc(n.groupNames.slice(0,6).join(', '))+'<\/div><div class=\"ss-ctt-sub\" style=\"margin-top:6px;\">Click for detail<\/div>';\n      } else if(n.type==='tactic'){\n        html = '<strong>'+esc(n.label)+'<\/strong><div class=\"ss-ctt-sub\">MITRE ATT&CK tactic'+(n.tacticId?' ('+esc(n.tacticId)+')':'')+' \u00b7 used by '+n.groupNames.length+' group(s)<br>'+esc(n.groupNames.slice(0,6).join(', '))+'<\/div><div class=\"ss-ctt-sub\" style=\"margin-top:6px;\">Click for detail<\/div>';\n      } else {\n        html = '<strong>'+esc(n.label)+'<\/strong>'\n          + '<div class=\"ss-ctt-sub\">'+(n.vendor||n.product?esc(n.vendor||'')+' '+esc(n.product||'')+'<br>':'')+(n.severity?esc(n.severity)+' severity':'')+(n.cvss?' \u00b7 CVSS '+esc(String(n.cvss)):'')+'<\/div>'\n          + '<div class=\"ss-ctt-sub\" style=\"margin-top:4px;\">Exploited by '+n.groupNames.length+' group(s)<br>'+esc(n.groupNames.slice(0,6).join(', '))+'<\/div>'\n          + '<div class=\"ss-ctt-sub\" style=\"margin-top:6px;\">Click for detail<\/div>';\n      }\n      tooltip.innerHTML = html;\n      tooltip.classList.add('ss-visible');\n      moveTooltip(evt);\n    }\n    function moveTooltip(evt){\n      var rect = wrap.getBoundingClientRect();\n      var x = evt.clientX-rect.left+14, y = evt.clientY-rect.top+14;\n      if(x+260>rect.width) x = evt.clientX-rect.left-274;\n      tooltip.style.left = x+'px';\n      tooltip.style.top = y+'px';\n    }\n    function hideTooltip(){ tooltip.classList.remove('ss-visible'); }\n\n    function connectedIds(n){\n      var ids = {};\n      ids[n.id] = true;\n      links.forEach(function(l){\n        var s=(l.source&&l.source.id)||l.source, t=(l.target&&l.target.id)||l.target;\n        if(s===n.id) ids[t]=true;\n        if(t===n.id) ids[s]=true;\n      });\n      return ids;\n    }\n\n    \/\/ Au survol : les bulles non liees se retrecissent (plutot que de\n    \/\/ simplement s'estomper) pour degager de la place autour du noeud\n    \/\/ regarde, comme demande. La simulation garde le rayon de base pour\n    \/\/ ses calculs de collision ; seule l'attribut SVG \"r\" est anime ici.\n    function displayRadius(n, ids){\n      var base = nodeRadius(n);\n      if(!ids) return base;\n      return ids[n.id] ? base*1.12 : base*0.45;\n    }\n\n    \/\/ Tant que l'encadre de detail est ouvert, l'infobulle au survol est\n    \/\/ desactivee : sinon, l'infobulle (qui suit la souris, au-dessus de\n    \/\/ tout le reste) finit par recouvrir visuellement l'encadre des qu'on\n    \/\/ survole une bulle, donnant l'impression qu'il \"disparait\" alors\n    \/\/ qu'il reste simplement cache dessous.\n    var panelOpen = false;\n\n    \/\/ Etat de gel courant : ids = tous les noeuds actuellement figes (le\n    \/\/ groupe ancre inclus), anchorId = le groupe a l'origine du gel. Les\n    \/\/ deux restent null hors de tout gel.\n    var frozenIds = null;\n    var frozenAnchorId = null;\n\n    nodeSel.on('mouseenter', function(evt,n){\n      \/\/ Pendant un gel, l'apparence et l'interactivite sont entierement\n      \/\/ pilotees par applyFreezeVisuals() : le survol ponctuel ne doit ni\n      \/\/ l'ecraser, ni s'appliquer aux bulles deja non cliquables.\n      if(frozenIds) return;\n      var ids = connectedIds(n);\n      nodeSel.transition().duration(140).attr('r', function(m){ return displayRadius(m, ids); }).attr('opacity', function(m){ return ids[m.id]?1:0.35; });\n      linkSel.transition().duration(140).attr('opacity', function(l){\n        var s=(l.source&&l.source.id)||l.source, t=(l.target&&l.target.id)||l.target;\n        return (s===n.id||t===n.id)?0.9:0.06;\n      });\n      labelGroupSel.transition().duration(140).attr('opacity', function(m){ return ids[m.id]?1:0.25; });\n      if(!panelOpen) showTooltip(evt,n);\n    }).on('mousemove', function(evt){ if(!panelOpen) moveTooltip(evt); })\n      .on('mouseleave', function(){\n      if(frozenIds) return;\n      nodeSel.transition().duration(180).attr('r', function(m){ return displayRadius(m, null); }).attr('opacity',1);\n      linkSel.transition().duration(180).attr('opacity',0.6);\n      labelGroupSel.transition().duration(180).attr('opacity',1);\n      hideTooltip();\n    }).on('click', function(evt,n){\n      if(frozenIds && !frozenIds[n.id]) return; \/\/ securite : deja non cliquable via pointer-events\n      hideTooltip();\n      if(n.type==='group'){\n        freezeGroup(n);\n      } else {\n        \/\/ Bulle liee au groupe actuellement fige : on affiche son propre\n        \/\/ detail sans toucher au gel en cours, pour pouvoir enchainer\n        \/\/ plusieurs bulles liees les unes apres les autres. Une bulle\n        \/\/ cliquee hors de tout gel affiche simplement son detail seul.\n        showDetailPanel(n);\n      }\n    });\n\n    \/\/ Fige en position le groupe cliqu\u00e9 et tous ses elements directement\n    \/\/ lies (outils, tactiques, CVE), qui restent alors cliquables un par\n    \/\/ un pendant que l'encadre reste ouvert. Toutes les autres bulles\n    \/\/ restent en retrait, visuellement et fonctionnellement : reduites,\n    \/\/ estompees et rendues non cliquables (pointer-events:none) tant que\n    \/\/ ce gel n'est pas relache. Un gel precedent est toujours relache\n    \/\/ avant d'en poser un nouveau.\n    function freezeGroup(n){\n      releaseFreeze();\n      var ids = connectedIds(n);\n      nodes.forEach(function(m){\n        if(ids[m.id]){ m.fx = m.x; m.fy = m.y; }\n      });\n      frozenIds = ids;\n      frozenAnchorId = n.id;\n      nodeSel.classed('ss-frozen', function(m){ return !!ids[m.id]; });\n      applyFreezeVisuals(ids);\n      showDetailPanel(n);\n    }\n\n    \/\/ Applique l'etat visuel et fonctionnel du gel : les bulles liees\n    \/\/ restent pleinement visibles et cliquables, toutes les autres passent\n    \/\/ en retrait (taille et opacite reduites) et perdent tout evenement de\n    \/\/ pointeur, donc tout survol comme tout clic.\n    function applyFreezeVisuals(ids){\n      nodeSel.transition().duration(160)\n        .attr('r', function(m){ return displayRadius(m, ids); })\n        .attr('opacity', function(m){ return ids[m.id]?1:0.28; });\n      nodeSel.style('pointer-events', function(m){ return ids[m.id] ? null : 'none'; });\n      linkSel.transition().duration(160).attr('opacity', function(l){\n        var s=(l.source&&l.source.id)||l.source, t=(l.target&&l.target.id)||l.target;\n        return (ids[s]&&ids[t])?0.9:0.05;\n      });\n      labelGroupSel.transition().duration(160).attr('opacity', function(m){ return ids[m.id]?1:0.2; });\n    }\n\n    \/\/ Relache tout gel en cours (n\u0153uds repris par la simulation, apparence\n    \/\/ et interactivite normales restaurees) sans rouvrir ni fermer\n    \/\/ l'encadre : utilise a la fois avant un nouveau gel et a la fermeture\n    \/\/ complete de l'encadre.\n    function releaseFreeze(){\n      if(frozenIds){\n        nodes.forEach(function(m){\n          if(frozenIds[m.id]){ m.fx = null; m.fy = null; }\n        });\n        nodeSel.classed('ss-frozen', false);\n        nodeSel.style('pointer-events', null);\n        nodeSel.transition().duration(160).attr('r', function(m){ return displayRadius(m, null); }).attr('opacity',1);\n        linkSel.transition().duration(160).attr('opacity',0.6);\n        labelGroupSel.transition().duration(160).attr('opacity',1);\n      }\n      frozenIds = null;\n      frozenAnchorId = null;\n    }\n\n    \/\/ Encadre de detail au clic : reste affiche (pas de redirection), sans\n    \/\/ aucun lien vers une autre page. Le detail du groupe indique en plus\n    \/\/ que ses elements lies restent cliquables tant que l'encadre est\n    \/\/ ouvert.\n    function showDetailPanel(n){\n      panelOpen = true;\n      var html = '';\n      if(n.type==='group'){\n        html = '<div class=\"ss-hub-constellation-detail-title\">'+esc(n.label)+(n.altname?' <span style=\"font-weight:400;color:#8a8a9a;\">('+esc(n.altname)+')<\/span>':'')+'<\/div>'\n          + '<div class=\"ss-hub-constellation-detail-sub\">'+(n.victims||0).toLocaleString('en-GB')+' victims'+(n.country?' \u00b7 '+esc(n.country):'')+'<\/div>'\n          + (n.description?'<div class=\"ss-hub-constellation-detail-body\">'+esc(n.description.slice(0,220))+(n.description.length>220?'\u2026':'')+'<\/div>':'')\n          + '<div class=\"ss-hub-constellation-detail-sub\" style=\"margin-top:10px;\">Its linked tools, tactics and CVEs are now pinned below \u2014 click one for its own detail.<\/div>';\n      } else if(n.type==='tool'){\n        html = '<div class=\"ss-hub-constellation-detail-title\">'+esc(n.label)+'<\/div>'\n          + '<div class=\"ss-hub-constellation-detail-sub\">Tool \u00b7 used by '+n.groupNames.length+' group(s)<\/div>'\n          + '<div class=\"ss-hub-constellation-detail-body\">'+esc(n.groupNames.join(', '))+'<\/div>';\n      } else if(n.type==='tactic'){\n        html = '<div class=\"ss-hub-constellation-detail-title\">'+esc(n.label)+'<\/div>'\n          + '<div class=\"ss-hub-constellation-detail-sub\">MITRE ATT&CK tactic'+(n.tacticId?' ('+esc(n.tacticId)+')':'')+' \u00b7 used by '+n.groupNames.length+' group(s)<\/div>'\n          + '<div class=\"ss-hub-constellation-detail-body\">'+esc(n.groupNames.join(', '))+'<\/div>';\n      } else {\n        html = '<div class=\"ss-hub-constellation-detail-title\">'+esc(n.label)+'<\/div>'\n          + '<div class=\"ss-hub-constellation-detail-sub\">'+(n.vendor||n.product?esc(n.vendor||'')+' '+esc(n.product||''):'')+(n.severity?' \u00b7 '+esc(n.severity):'')+(n.cvss?' \u00b7 CVSS '+esc(String(n.cvss)):'')+'<\/div>'\n          + (n.kevDesc?'<div class=\"ss-hub-constellation-detail-body\">'+esc(n.kevDesc.slice(0,180))+(n.kevDesc.length>180?'\u2026':'')+'<\/div>':'')\n          + '<div class=\"ss-hub-constellation-detail-body\">Exploited by '+n.groupNames.length+' group(s): '+esc(n.groupNames.join(', '))+'<\/div>';\n      }\n      detailContent.innerHTML = html;\n      detailPanel.classList.add('ss-visible');\n    }\n\n    \/\/ Fermeture de l'encadre : relache le gel en cours et relance la\n    \/\/ simulation, pour que le graphe revienne entierement a son etat\n    \/\/ initial, comme demande.\n    function hideDetailPanel(){\n      panelOpen = false;\n      detailPanel.classList.remove('ss-visible');\n      releaseFreeze();\n      simulation.alpha(0.35).restart();\n    }\n\n    var simulation = d3.forceSimulation(nodes)\n      .force('link', d3.forceLink(links).id(function(d){ return d.id; }).distance(function(l){ return l.kind==='tool'?48:(l.kind==='tactic'?58:52); }).strength(0.5))\n      .force('charge', d3.forceManyBody().strength(-120))\n      .force('center', d3.forceCenter(W\/2, H\/2))\n      .force('collide', d3.forceCollide(function(n){ return nodeRadius(n)+4; }))\n      .on('tick', ticked);\n\n    \/\/ Maintient chaque noeud (et son etiquette eventuelle) dans le cadre\n    \/\/ visible du graphe : sans cela, les bulles les plus excentrees restent\n    \/\/ hors champ (bord haut notamment) et ne sont jamais visibles ni\n    \/\/ cliquables, quelle que soit la taille du graphe.\n    function clampNodes(){\n      nodes.forEach(function(d){\n        var r = nodeRadius(d)+4;\n        var topPad = d.type==='group' ? r+22 : r;\n        if(d.x < r) d.x = r; else if(d.x > W-r) d.x = W-r;\n        if(d.y < topPad) d.y = topPad; else if(d.y > H-r) d.y = H-r;\n      });\n    }\n\n    function ticked(){\n      clampNodes();\n      linkSel.attr('x1', function(d){ return d.source.x; }).attr('y1', function(d){ return d.source.y; })\n        .attr('x2', function(d){ return d.target.x; }).attr('y2', function(d){ return d.target.y; });\n      nodeSel.attr('cx', function(d){ return d.x; }).attr('cy', function(d){ return d.y; });\n      labelGroupSel.attr('transform', function(d){ return 'translate('+d.x+','+d.y+')'; });\n    }\n\n    var drag = d3.drag()\n      .on('start', function(evt,d){ if(!evt.active) simulation.alphaTarget(0.25).restart(); d.fx=d.x; d.fy=d.y; })\n      .on('drag', function(evt,d){ d.fx=evt.x; d.fy=evt.y; })\n      .on('end', function(evt,d){ if(!evt.active) simulation.alphaTarget(0); d.fx=null; d.fy=null; });\n    nodeSel.call(drag);\n  }\n\n  function iconBug(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><path d=\"M12 3v3M12 18v3M4.5 12h3M16.5 12h3M6.5 6.5l2 2M15.5 15.5l2 2M17.5 6.5l-2 2M8.5 15.5l-2 2\"\/><circle cx=\"12\" cy=\"12\" r=\"4\"\/><\/svg>'; }\n  function iconShield(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><path d=\"M12 3l8 4v5c0 4.5-3.2 7.9-8 9-4.8-1.1-8-4.5-8-9V7z\"\/><\/svg>'; }\n  function iconDb(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><ellipse cx=\"12\" cy=\"6\" rx=\"7\" ry=\"2.5\"\/><path d=\"M5 6v12c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5V6\"\/><path d=\"M5 12c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5\"\/><\/svg>'; }\n  function iconGavel(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><path d=\"M12 3v18M7 21h10M5 7l3-2 3 2-3 6-3-6zM16 7l3-2 3 2-3 6-3-6z\"\/><\/svg>'; }\n  function iconHook(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><path d=\"M8 3v10a4 4 0 0 0 8 0v-2.5\"\/><circle cx=\"8\" cy=\"3\" r=\"1.6\" fill=\"'+c+'\" stroke=\"none\"\/><\/svg>'; }\n  function iconServer(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"6\" rx=\"1.2\"\/><rect x=\"3\" y=\"14\" width=\"18\" height=\"6\" rx=\"1.2\"\/><circle cx=\"7\" cy=\"7\" r=\"0.8\" fill=\"'+c+'\" stroke=\"none\"\/><circle cx=\"7\" cy=\"17\" r=\"0.8\" fill=\"'+c+'\" stroke=\"none\"\/><\/svg>'; }\n  function iconGlobe(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"9\"\/><path d=\"M3 12h18M12 3c2.5 2.5 4 6 4 9s-1.5 6.5-4 9c-2.5-2.5-4-6-4-9s1.5-6.5 4-9z\"\/><\/svg>'; }\n  function iconBulletin(c){ return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"'+c+'\" stroke-width=\"2\"><path d=\"M7 3h8l4 4v14H7z\"\/><path d=\"M15 3v4h4\"\/><path d=\"M9.5 12h6M9.5 15.5h6M9.5 8.5h3\"\/><\/svg>'; }\n\n  \/* ---------------------------------------------------------------\n     Fetch and boot\n  --------------------------------------------------------------- *\/\n  function boot(){\n    if(typeof SS_CONFIG === 'undefined'){\n      var el = document.getElementById('ss-hub-crisisroom');\n      if(el) el.innerHTML = '<div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Configuration unavailable.<\/div>';\n      return;\n    }\n    var url = SS_CONFIG.ajax_url + '?action=ss_get_data&section=all&nonce=' + encodeURIComponent(SS_CONFIG.nonce);\n    fetch(url).then(function(r){ return r.json(); }).then(function(json){\n      if(!json || !json.success){ throw new Error('bad response'); }\n      SSHub.render(json.data);\n    }).catch(function(){\n      var el = document.getElementById('ss-hub-crisisroom');\n      if(el) el.innerHTML = '<div style=\"padding:40px;text-align:center;color:#8a8a9a;\">Unable to load live data.<\/div>';\n    });\n  }\n\n  if(document.readyState === 'loading'){\n    document.addEventListener('DOMContentLoaded', boot);\n  } else {\n    boot();\n  }\n\n})(window.SSHub);\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9a5b21a syd-sticky-section-no elementor-widget elementor-widget-html\" data-id=\"9a5b21a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<script type=\"application\/ld+json\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebApplication\",\"name\":\"Stroople Signal\",\"url\":\"https:\/\/www.stroople.com\/signal\/\",\"description\":\"Free cyber threat intelligence dashboard tracking ransomware victims, CISA KEV vulnerabilities, data breaches and CNIL sanctions in France and worldwide, updated automatically from public sources.\",\"applicationCategory\":\"SecurityApplication\",\"operatingSystem\":\"Web\",\"inLanguage\":\"en\",\"isAccessibleForFree\":true,\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"EUR\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Stroople\",\"url\":\"https:\/\/www.stroople.com\/\"},\"hasPart\":[{\"@type\":\"WebPage\",\"name\":\"Ransomware Tracker\",\"url\":\"https:\/\/www.stroople.com\/signal-ransomware\/\"},{\"@type\":\"WebPage\",\"name\":\"Vulnerabilities Tracker (CISA KEV)\",\"url\":\"https:\/\/www.stroople.com\/signal-vulnerabilities-kev\/\"},{\"@type\":\"WebPage\",\"name\":\"Data Breaches Tracker\",\"url\":\"https:\/\/www.stroople.com\/signal-data-breaches\/\"},{\"@type\":\"WebPage\",\"name\":\"CNIL Sanctions Tracker\",\"url\":\"https:\/\/www.stroople.com\/signal-cnil-sanctions-france\/\"}]},{\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Stroople\",\"item\":\"https:\/\/www.stroople.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Signal\",\"item\":\"https:\/\/www.stroople.com\/signal\/\"}]}]}<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4a7e633 syd-sticky-section-no elementor-widget elementor-widget-html\" data-id=\"4a7e633\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<script type=\"application\/ld+json\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebApplication\",\"name\":\"Stroople Signal\",\"url\":\"https:\/\/www.stroople.com\/fr\/signal\/\",\"description\":\"Tableau de bord gratuit de veille cyber, suivant les victimes de ransomware, les vuln\u00e9rabilit\u00e9s du catalogue CISA KEV, les fuites de donn\u00e9es et les sanctions CNIL en France et dans le monde, mis \u00e0 jour automatiquement depuis des sources publiques.\",\"applicationCategory\":\"SecurityApplication\",\"operatingSystem\":\"Web\",\"inLanguage\":\"fr\",\"isAccessibleForFree\":true,\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"EUR\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Stroople\",\"url\":\"https:\/\/www.stroople.com\/\"},\"hasPart\":[{\"@type\":\"WebPage\",\"name\":\"Suivi Ransomware\",\"url\":\"https:\/\/www.stroople.com\/fr\/signal-ransomware\/\"},{\"@type\":\"WebPage\",\"name\":\"Suivi des vuln\u00e9rabilit\u00e9s (CISA KEV)\",\"url\":\"https:\/\/www.stroople.com\/fr\/signal-vulnerabilities-kev\/\"},{\"@type\":\"WebPage\",\"name\":\"Suivi des fuites de donn\u00e9es\",\"url\":\"https:\/\/www.stroople.com\/fr\/signal-data-breaches\/\"},{\"@type\":\"WebPage\",\"name\":\"Suivi des sanctions CNIL\",\"url\":\"https:\/\/www.stroople.com\/fr\/signal-cnil-sanctions-france\/\"}]},{\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Stroople\",\"item\":\"https:\/\/www.stroople.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Signal\",\"item\":\"https:\/\/www.stroople.com\/fr\/signal\/\"}]}]}<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t<div class=\"elementor-element elementor-element-cbae25a e-flex e-con-boxed syd-sticky-section-no e-con e-parent\" data-id=\"cbae25a\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-239b371 syd-sticky-section-no elementor-widget elementor-widget-html\" data-id=\"239b371\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div id=\"ss-lang-switch\" style=\"position:fixed;bottom:80px;right:24px;z-index:9999;display:flex;gap:6px\"><\/div>\n<script>\n(function(){\n  var isFrench = window.location.pathname.indexOf('\/fr\/') !== -1;\n  var active = {background:'#09025F', color:'#fff'};\n  var inactive = {background:'#f1efe8', color:'#5f5e5a'};\n\n  var wrap = document.getElementById('ss-lang-switch');\n  if(!wrap) return;\n\n  function makeLink(url, label, isActive){\n    var a = document.createElement('a');\n    a.href = url;\n    a.textContent = label;\n    a.style.fontSize = '11px';\n    a.style.fontWeight = '500';\n    a.style.padding = '6px 12px';\n    a.style.borderRadius = '6px';\n    a.style.textDecoration = 'none';\n    a.style.fontFamily = '-apple-system,sans-serif';\n    a.style.background = isActive ? active.background : inactive.background;\n    a.style.color = isActive ? active.color : inactive.color;\n    return a;\n  }\n\n  var enUrl = 'https:\/\/www.stroople.com\/signal\/';\n  var frUrl = 'https:\/\/www.stroople.com\/fr\/signal\/';\n\n  wrap.appendChild(makeLink(enUrl, '\\u{1F1EC}\\u{1F1E7} EN', !isFrench));\n  wrap.appendChild(makeLink(frUrl, '\\u{1F1EB}\\u{1F1F7} FR', isFrench));\n})();\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Signal Dashboard About the data Stroople LIVE CERT-FR&nbsp;\/&nbsp;ANSSI Loading official security alerts&#8230; \u2630 Menu Signal Monitoring Ransomware Vulnerabilities Data Breaches CNIL Sanctions [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":28148,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-28146","page","type-page","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Stroople Signal - Stroople<\/title>\n<meta name=\"description\" content=\"Ransomware victims, exploited CVEs, data breaches and CNIL fines: free cyber threat intelligence on France.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.stroople.com\/fr\/signal\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Stroople Signal - Stroople\" \/>\n<meta property=\"og:description\" content=\"Ransomware victims, exploited CVEs, data breaches and CNIL fines: free cyber threat intelligence on France.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.stroople.com\/fr\/signal\/\" \/>\n<meta property=\"og:site_name\" content=\"Stroople\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-20T16:06:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/06\/stroople-signal-cyber-threat-intelligence-france.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"640\" \/>\n\t<meta property=\"og:image:height\" content=\"360\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@Stroople1\" \/>\n<meta name=\"twitter:label1\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data1\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/\",\"url\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/\",\"name\":\"Stroople Signal - Stroople\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.stroople.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.stroople.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/stroople-signal-cyber-threat-intelligence-france.jpg\",\"datePublished\":\"2026-06-10T07:18:06+00:00\",\"dateModified\":\"2026-09-20T16:06:49+00:00\",\"description\":\"Ransomware victims, exploited CVEs, data breaches and CNIL fines: free cyber threat intelligence on France.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.stroople.com\\\/signal\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.stroople.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/stroople-signal-cyber-threat-intelligence-france.jpg\",\"contentUrl\":\"https:\\\/\\\/www.stroople.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/stroople-signal-cyber-threat-intelligence-france.jpg\",\"width\":640,\"height\":360,\"caption\":\"Stroople Signal \u2014 France cyber threat intelligence live dashboard\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.stroople.com\\\/signal\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.stroople.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Stroople Signal\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.stroople.com\\\/#website\",\"url\":\"https:\\\/\\\/www.stroople.com\\\/\",\"name\":\"Stroople\",\"description\":\"Cybersecurity Expertise\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.stroople.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.stroople.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.stroople.com\\\/#organization\",\"name\":\"Stroople Cybersecurity\",\"alternateName\":\"Stroople\",\"url\":\"https:\\\/\\\/www.stroople.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/www.stroople.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.stroople.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Logo-Stroople-responsive.png\",\"contentUrl\":\"https:\\\/\\\/www.stroople.com\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/Logo-Stroople-responsive.png\",\"width\":1000,\"height\":1000,\"caption\":\"Stroople Cybersecurity\"},\"image\":{\"@id\":\"https:\\\/\\\/www.stroople.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/Stroople1\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/stroople\\\/\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UC_kTnaswN04kjIrHU0Pi_qg\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Signal Stroople - Stroople","description":"Victimes de ran\u00e7ongiciels, vuln\u00e9rabilit\u00e9s exploit\u00e9es (CVE), violations de donn\u00e9es et amendes de la CNIL : renseignements gratuits sur les cybermenaces en France.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.stroople.com\/fr\/signal\/","og_locale":"fr_FR","og_type":"article","og_title":"Stroople Signal - Stroople","og_description":"Ransomware victims, exploited CVEs, data breaches and CNIL fines: free cyber threat intelligence on France.","og_url":"https:\/\/www.stroople.com\/fr\/signal\/","og_site_name":"Stroople","article_modified_time":"2026-09-20T16:06:49+00:00","og_image":[{"width":640,"height":360,"url":"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/06\/stroople-signal-cyber-threat-intelligence-france.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_site":"@Stroople1","twitter_misc":{"Dur\u00e9e de lecture estim\u00e9e":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.stroople.com\/signal\/","url":"https:\/\/www.stroople.com\/signal\/","name":"Signal Stroople - Stroople","isPartOf":{"@id":"https:\/\/www.stroople.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.stroople.com\/signal\/#primaryimage"},"image":{"@id":"https:\/\/www.stroople.com\/signal\/#primaryimage"},"thumbnailUrl":"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/06\/stroople-signal-cyber-threat-intelligence-france.jpg","datePublished":"2026-06-10T07:18:06+00:00","dateModified":"2026-09-20T16:06:49+00:00","description":"Victimes de ran\u00e7ongiciels, vuln\u00e9rabilit\u00e9s exploit\u00e9es (CVE), violations de donn\u00e9es et amendes de la CNIL : renseignements gratuits sur les cybermenaces en France.","breadcrumb":{"@id":"https:\/\/www.stroople.com\/signal\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.stroople.com\/signal\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/www.stroople.com\/signal\/#primaryimage","url":"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/06\/stroople-signal-cyber-threat-intelligence-france.jpg","contentUrl":"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/06\/stroople-signal-cyber-threat-intelligence-france.jpg","width":640,"height":360,"caption":"Stroople Signal \u2014 France cyber threat intelligence live dashboard"},{"@type":"BreadcrumbList","@id":"https:\/\/www.stroople.com\/signal\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.stroople.com\/"},{"@type":"ListItem","position":2,"name":"Stroople Signal"}]},{"@type":"WebSite","@id":"https:\/\/www.stroople.com\/#website","url":"https:\/\/www.stroople.com\/","name":"Stroople","description":"Expertise en cybers\u00e9curit\u00e9","publisher":{"@id":"https:\/\/www.stroople.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.stroople.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/www.stroople.com\/#organization","name":"Stroople Cybers\u00e9curit\u00e9","alternateName":"Stroople","url":"https:\/\/www.stroople.com\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/www.stroople.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/05\/Logo-Stroople-responsive.png","contentUrl":"https:\/\/www.stroople.com\/wp-content\/uploads\/2026\/05\/Logo-Stroople-responsive.png","width":1000,"height":1000,"caption":"Stroople Cybersecurity"},"image":{"@id":"https:\/\/www.stroople.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/Stroople1","https:\/\/www.linkedin.com\/company\/stroople\/","https:\/\/www.youtube.com\/channel\/UC_kTnaswN04kjIrHU0Pi_qg"]}]}},"_links":{"self":[{"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/pages\/28146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/comments?post=28146"}],"version-history":[{"count":229,"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/pages\/28146\/revisions"}],"predecessor-version":[{"id":30809,"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/pages\/28146\/revisions\/30809"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/media\/28148"}],"wp:attachment":[{"href":"https:\/\/www.stroople.com\/fr\/wp-json\/wp\/v2\/media?parent=28146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}