Search

Filter Data Breaches

Time

Explore relations between cyber attackers and their targets.

Filter Data Breaches
View live example →

Filtering complex data with KeyLines lets you go from a wide angle view of a vast data landscape to a focused exploration of key items.

This demo features data from the Verizon Data Breach Investigations Report (DBIR) which looks at thousands of data breaches across the world and examines attackers, attack vectors, and victims.

KeyLines features can identify broad trends right away:

  • Clicking Size Companies sizes the nodes representing victims in proportion to the number of times they were attacked.
  • Links are colour-coded by attack vector. Selecting Advanced tech reveals that the Activist group favour this attack vector. In contrast, selecting Basic Tech highlights their use by End-user or regular employees.
  • The time bar histogram and navigation controls do more than just show when attacks happened. Select multiple attack vectors to compare their overlaid trend lines with the aggregate volume of attacks to identify patterns.
  • Donuts on attacker nodes consist of colour-coded segments that show the relative proportion of attack vectors used. You can zoom in and examine them or use the time bar navigation controls to see how the proportions change over time.

Key functions used:

import KeyLines from "keylines";
import { data } from "./data.js";

let chart;
let timebar;
let suppressedChecks = [];

const linkColours = [
  "rgba(255, 0, 13, 0.7)",
  "rgba(252, 132, 39, 0.7)",
  "rgba(255, 207, 9, 0.7)",
  "rgba(33, 252, 13, 0.7)",
  "rgba(0, 253, 255, 0.7)",
  "rgba(229, 153, 255, 0.7)",
  "rgba(227, 19, 254, 0.7)",
  "rgba(186, 153, 15, 0.7)",
];

const orange = "rgb(255, 127, 14)";

const typeToCategories = {
  advanced: {
    "Web application": 1,
    "Remote access": 1,
    "Backdoor or C2": 1,
    "Command shell": 1,
    VPN: 1,
    "Web drive-by": 1,
  },
  basic: {
    "LAN access": 1,
    "Desktop sharing": 1,
    Phone: 1,
    Documents: 1,
    "Direct install": 1,
    "3rd party desktop": 1,
    "Software update": 1,
  },
  careless: {
    Carelessness: 1,
    "Inadequate processes": 1,
    "Inadequate technology": 1,
    "Non-corporate": 1,
  },
  vArea: {
    "Victim work area": 1,
    "Victim public area": 1,
    "Victim grounds": 1,
    "Victim secure area": 1,
  },
  tArea: { "Public facility": 1, "Partner facility": 1, "Public vehicle": 1 },
  email: {
    Email: 1,
    "Email attachment": 1,
    "Email autoexecute": 1,
    "Email link": 1,
  },
  physical: {
    "Physical access": 1,
    "Personal residence": 1,
    "Personal vehicle": 1,
    "In-person": 1,
  },
  unknown: { Unknown: 1, Other: 1, "Random error": 1 },
};

// later we'll reverse the dictionary above to quickly filter items
const categoryToType = {};

function linkColoursByNode(nodes, links) {
  const linksByNode = {};
  nodes.forEach((node) => {
    linksByNode[node.id] = [];
  });

  links.forEach((link) => {
    if (link && link.c) {
      const linkColour = link.c;
      if (linksByNode[link.id1]) {
        linksByNode[link.id1].push(linkColour);
      }
      if (linksByNode[link.id2]) {
        linksByNode[link.id2].push(linkColour);
      }
    }
  });

  return linksByNode;
}

/**
 * Given an async function (fn), this function returns
 * a new function that will queue up to 1 call to fn when
 * invoked concurrently.
 */
function asyncThrottle(fn) {
  // 0 = ready, 1 = running, 2 = queued
  let state = 0;

  const run = async () => {
    if (state > 0) {
      state = 2;
    } else {
      state = 1;
      await fn();
      const queued = state > 1;
      state = 0;
      if (queued) run();
    }
  };

  return run;
}

function doLayout(mode) {
  return chart.layout("organic", {
    time: 300,
    easing: "linear",
    mode,
    tightness: 8,
  });
}

function getDonutsForActorNodes(nodes, linksByNode) {
  return nodes.map((node) => {
    const donutValues = [0, 0, 0, 0, 0, 0, 0, 0];
    const linkColourList = linksByNode[node.id];
    if (linkColourList) {
      linkColourList.forEach((linkColour) => {
        const index = linkColours.indexOf(linkColour);
        if (index !== -1) {
          donutValues[index]++;
        }
      });
    }
    return { id: node.id, donut: { v: donutValues } };
  });
}

function getActorNodeIdsInRange(linkIdsInRange) {
  const allNodeIds = [];
  linkIdsInRange.forEach((id) => {
    const link = chart.getItem(id);
    if (link && !allNodeIds.includes(link.id1)) allNodeIds.push(link.id1);
    if (link && !allNodeIds.includes(link.id2)) allNodeIds.push(link.id2);
  });

  return allNodeIds.filter((id) => {
    const node = chart.getItem(id);
    return node.d.type === "actor";
  });
}

function updateDonuts() {
  const range = timebar.range();

  // find the attacks that occured within the timebar's range
  const linkIdsInRange = timebar.getIds(range.dt1, range.dt2);
  const linksInRange = chart.getItem(linkIdsInRange);

  // find the actor nodes that are adjacent to those links
  const actorNodeIdsInRange = getActorNodeIdsInRange(linkIdsInRange);
  const actorNodesInRange = chart.getItem(actorNodeIdsInRange);

  // get an object listing all the link colours for each of those actor nodes
  const actorLinkColours = linkColoursByNode(actorNodesInRange, linksInRange);

  // use the lists of link colours to make donuts for those actor nodes
  const donutsToUpdate = getDonutsForActorNodes(
    actorNodesInRange,
    actorLinkColours,
  );
  chart.animateProperties(donutsToUpdate, { time: 300, easing: "cubic" });
}

function resetTimebarSelection() {
  timebar.selection([]);
}

function itemsAndNeighbours(ids) {
  const result = {};
  const items = chart.getItem(ids);

  const neighbourIds = chart.graph().neighbours(ids, { all: true });

  neighbourIds.links.concat(neighbourIds.nodes).forEach((neighbourId) => {
    result[neighbourId] = true;
  });

  items.forEach((item) => {
    result[item.id] = true;
  });
  return result;
}

function neighbouringCriterion(ids) {
  const idsToForeground = itemsAndNeighbours(ids);
  return (item) => idsToForeground[item.id];
}

// foreground/background the chart items based on whether they neighbour a checked attack vector
function foregroundCheckedAttackVectors() {
  // make all checkboxes determinate
  document.querySelectorAll(".vector input").forEach((checkbox) => {
    checkbox.indeterminate = false;
  });
  // re-check suppressed checkboxes
  suppressedChecks.forEach((checkbox) => {
    checkbox.checked = true;
  });
  suppressedChecks = [];

  const threshold = 3;

  // first read the number of checkboxes checked
  const checked = document.querySelectorAll(".vector input:checked");
  // if there are more than 3 uncheck this last one and return
  if (checked.length > threshold) {
    // just exit
    this.checked = false;
    return;
  }

  document
    .querySelectorAll(".vector input:not(:checked)")
    .forEach((checkbox) => {
      checkbox.disabled = checked.length === threshold;
    });

  resetTimebarSelection();

  const criteria = [];

  if (checked.length) {
    const checkedTypes = {};

    checked.forEach((el, i) => {
      // save type -> selection object in this dictionary
      checkedTypes[el.id] = {
        id: [],
        index: i,
        c: el.parentElement.querySelector("span.color-legend").style
          .backgroundColor,
      };
    });

    chart.each({ type: "link" }, (link) => {
      const type = categoryToType[link.d.type];
      if (type in checkedTypes) {
        checkedTypes[type].id.push(link.id);
      }
    });

    const selectionList = [];

    Object.keys(checkedTypes).forEach((index) => {
      const ids = checkedTypes[index].id;

      criteria.push(neighbouringCriterion(ids));

      selectionList.push(checkedTypes[index]);
    });

    timebar.selection(selectionList);

    // foreground the checked vectors
    chart.foreground((item) => criteria.some((criterion) => criterion(item)));
  } else {
    // no vector checkboxes are checked, so foreground everything
    chart.foreground(() => true);
  }
}

function forEachVictimNode(fn) {
  chart.each({ type: "node" }, (node) => {
    if (node.d.type === "victim") {
      fn(node);
    }
  });
}

async function resetVector(e) {
  // reset the checkboxes
  document.querySelectorAll(".vector input").forEach((input) => {
    input.checked = false;
    input.disabled = false;
    input.indeterminate = false;
  });
  // clear the chart selection
  chart.selection([]);

  resetTimebarSelection();
  chart.foreground(() => true);
  // reset the size of companies as well
  const changes = [];
  forEachVictimNode((node) => {
    changes.push({ id: node.id, e: 1 });
  });
  await chart.animateProperties(changes, {});
  doLayout("adaptive");
  e.preventDefault();
}

function normalize(value, min, max) {
  if (min === max) {
    return value;
  }
  return (value - min) / (max - min);
}

async function sizeByCompanyVectors() {
  const degrees = chart.graph().degrees();
  const changes = [];
  let max = -Infinity;
  let min = Infinity;
  // first pass: find the max and min degrees
  forEachVictimNode((node) => {
    if (node.id in degrees) {
      max = Math.max(max, degrees[node.id]);
      min = Math.min(min, degrees[node.id]);
    }
  });
  // second pass, now size the nodes
  forEachVictimNode((node) => {
    if (node.id in degrees) {
      changes.push({
        id: node.id,
        e: 1 + 6 * normalize(degrees[node.id], min, max),
      });
    }
  });
  await chart.animateProperties(changes, { time: 800 });
  doLayout("adaptive");
}

const filterOnTimebarChange = asyncThrottle(async () => {
  // filter the chart to show only items in the new range
  await chart.filter(timebar.inRange, { animate: false, type: "link" });
  updateDonuts();
  await doLayout("adaptive");
});

function foregroundOnSelectionChange() {
  resetTimebarSelection();
  const selection = chart.selection();

  if (selection.length) {
    // foreground the selected items, and any neighbours thereof
    const foreground = itemsAndNeighbours(selection);

    // find all the attack types that have a link in the foreground
    const selectedAttackTypes = [];
    chart.foreground(
      (link) => {
        if (foreground[link.id]) {
          const type = categoryToType[link.d.type];
          selectedAttackTypes.push(type);
          return true;
        }
        return false;
      },
      { type: "link" },
    );

    document.querySelectorAll(".vector input").forEach((checkbox) => {
      // for the selected attack types, make the corresponding checkboxes indeterminate
      checkbox.indeterminate = selectedAttackTypes.includes(checkbox.id);
      // uncheck any other checked checkboxes
      if (checkbox.checked && !checkbox.indeterminate) {
        checkbox.checked = false;
        // record that we unchecked this checkbox, so we can re-check it later
        suppressedChecks.push(checkbox);
      }
    });
  } else {
    // In this case, the click was on the chart background,
    // so we do the foregrounding in accordance with checkbox state.
    foregroundCheckedAttackVectors();
  }
}

async function klReady(components) {
  [chart, timebar] = components;

  chart.load(data);
  chart.zoom("fit");

  timebar.load(data);
  await timebar.zoom("fit", { time: 100 });

  // Setup Filters
  // when the time bar range changes, filter the chart accordingly
  timebar.on("change", filterOnTimebarChange);
  // handle clicks by foregrounding the selected item(s) and neighbours thereof
  chart.on("selection-change", foregroundOnSelectionChange);

  // reverse the typeToCategory dictionary
  Object.keys(typeToCategories).forEach((type) => {
    const categories = typeToCategories[type];
    Object.keys(categories).forEach((category) => {
      categoryToType[category] = type;
    });
  });

  // Vector Filter
  document.querySelectorAll(".vector input").forEach((input) => {
    input.addEventListener("change", foregroundCheckedAttackVectors, false);
    input.addEventListener("keyup", foregroundCheckedAttackVectors, false);
  });

  document
    .getElementById("reset")
    .addEventListener("click", resetVector, false);
  // add an explanation to the vector categories
  document.querySelectorAll(".vector").forEach((el) => {
    const categories =
      typeToCategories[el.querySelector("input").getAttribute("id")];
    const names = Object.keys(categories);
    const label = el.querySelector("span.text-legend");
    const wrapperSpan = el.querySelector("span.popover-wrapper");
    wrapperSpan.setAttribute("data-title", label.textContent);
    wrapperSpan.setAttribute("data-content", names.join(", "));
  });

  // Layout button
  document.getElementById("layout").addEventListener("click", doLayout, false);
  document
    .getElementById("degrees")
    .addEventListener("click", sizeByCompanyVectors, false);
}

async function startKeyLines() {
  const attackerIcon = "fas fa-users";
  const chartOptions = {
    controlTheme: "dark",
    drag: {
      links: false,
    },
    handMode: true,
    iconFontFamily: "Font Awesome 5 Free",
    overview: { icon: false, shown: false },
    minZoom: 0.01,
    selectionColour: orange,
    linkEnds: { avoidLabels: false },
    imageAlignment: {},
    backColour: "#2d383f",
  };

  chartOptions.imageAlignment[attackerIcon] = {
    e: 0.8,
  };

  const timeBarOptions = {
    area: { colour: "#FFFFFF" },
    backColour: "#2d383f",
    controlBarTheme: "dark",
    scale: { highlightColour: "#475259" },
    playSpeed: 50,
    sliders: "none",
    type: "area",
  };

  const components = await KeyLines.create([
    {
      container: "klchart",
      type: "chart",
      options: chartOptions,
    },
    {
      container: "kltimebar",
      type: "timebar",
      options: timeBarOptions,
    },
  ]);
  klReady(components);
}

function loadFontsAndStart() {
  document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}

window.addEventListener("DOMContentLoaded", loadFontsAndStart);
export const data = {
  type: "LinkChart",
  items: [
    {
      id: "Azerenerji:Activist:Web application",
      id1: "Azerenerji",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1378322940000],
      off: 0,
    },
    {
      id: "Combined Systems:Activist:Web application",
      id1: "Combined Systems",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1361242863000],
      off: 0,
    },
    {
      id: "Northrop Grumman Corporation:State-affiliated:Unknown",
      id1: "Northrop Grumman Corporation",
      id2: "State-affiliated",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1405192769000],
      off: 0,
    },
    {
      id: "Mangement of Aggressive Behavior Training International, Inc:Activist:Unknown",
      id1: "Mangement of Aggressive Behavior Training International, Inc",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1361243495000],
      off: 0,
    },
    {
      id: "Syrian Arab News Agency:Activist:Web application",
      id1: "Syrian Arab News Agency",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1379103720000],
      off: 0,
    },
    {
      id: "Eastern Buffet:Cashier:Physical access",
      id1: "Eastern Buffet",
      id2: "Cashier",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1363299196000],
      off: 0,
    },
    {
      id: "UConn Health Center:Former employee:Physical access",
      id1: "UConn Health Center",
      id2: "Former employee",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1387297020000],
      off: 0,
    },
    {
      id: "Alamance County Department of Social Services:Other:Physical access",
      id1: "Alamance County Department of Social Services",
      id2: "Other",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1415893560000],
      off: 0,
    },
    {
      id: "Telvent Canada Ltd:State-affiliated:Unknown",
      id1: "Telvent Canada Ltd",
      id2: "State-affiliated",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1416497640000],
      off: 0,
    },
    {
      id: "Medway Maritime Hospital:End-user:LAN access",
      id1: "Medway Maritime Hospital",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1418431403000],
      off: 20,
    },
    {
      id: "Flowers Hospital:End-user:Physical access",
      id1: "Flowers Hospital",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1398956640000],
      off: 0,
    },
    {
      id: "Plymouth City Council:System admin:Unknown",
      id1: "Plymouth City Council",
      id2: "System admin",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1408741440000],
      off: 0,
    },
    {
      id: "Alabama Book Store, Inc.:Organized crime:Web application",
      id1: "Alabama Book Store, Inc.",
      id2: "Organized crime",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1360947399000],
      off: 0,
    },
    {
      id: "Singapore Prime Minister's Office:Activist:Web application",
      id1: "Singapore Prime Minister's Office",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1384051680000],
      off: 0,
    },
    {
      id: "Coast Capital Savings Credit Union:Helpdesk:LAN access",
      id1: "Coast Capital Savings Credit Union",
      id2: "Helpdesk",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1377300960000],
      off: 0,
    },
    {
      id: "Creative Banner Assemblies:Organized crime:Unknown",
      id1: "Creative Banner Assemblies",
      id2: "Organized crime",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1378234860000],
      off: 0,
    },
    {
      id: "Teachers Insurance and Annuity Association - College Retirement Equities Fund:Organized crime:Web application",
      id1: "Teachers Insurance and Annuity Association - College Retirement Equities Fund",
      id2: "Organized crime",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1406041560000],
      off: 0,
    },
    {
      id: "NZ Government of New Zealand:Activist:Web application",
      id1: "NZ Government of New Zealand",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1379695740000],
      off: 0,
    },
    {
      id: "DJArts:Unaffiliated:Web application",
      id1: "DJArts",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1361992515000],
      off: 0,
    },
    {
      id: "Viber:Activist:Web application",
      id1: "Viber",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1377466227000],
      off: 0,
    },
    {
      id: "Kenya National Registration Bureau:Activist:Web application",
      id1: "Kenya National Registration Bureau",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1377011378000],
      off: 0,
    },
    {
      id: "NATO Cooperative Cyber Defence Centre of Excellence:Activist:Web application",
      id1: "NATO Cooperative Cyber Defence Centre of Excellence",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1384048500000],
      off: 0,
    },
    {
      id: "Citywide Mortgage:Finance:LAN access",
      id1: "Citywide Mortgage",
      id2: "Finance",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1407854160000],
      off: 0,
    },
    {
      id: "Bon Secours DePaul Medical Center:End-user:LAN access",
      id1: "Bon Secours DePaul Medical Center",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1370369131000],
      off: 0,
    },
    {
      id: "Bon Secours DePaul Medical Center:Unaffiliated:LAN access",
      id1: "Bon Secours DePaul Medical Center",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1370369131000],
      off: 0,
    },
    {
      id: "Government of Albania:Unaffiliated:Web application",
      id1: "Government of Albania",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1394220720000],
      off: 0,
    },
    {
      id: "Alexza Pharmaceuticals:Activist:Web application",
      id1: "Alexza Pharmaceuticals",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1388693961000],
      off: 0,
    },
    {
      id: "PS Palestine Ministry of Justice:Activist:Web application",
      id1: "PS Palestine Ministry of Justice",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1381141380000],
      off: 0,
    },
    {
      id: "LexisNexis:Organized crime:Web application",
      id1: "LexisNexis",
      id2: "Organized crime",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1380121860000],
      off: 0,
    },
    {
      id: "University of New Haven:Other:Unknown",
      id1: "University of New Haven",
      id2: "Other",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1388958060000],
      off: 0,
    },
    {
      id: "Home Office (Govt. of the United Kingdom):System admin:Unknown",
      id1: "Home Office (Govt. of the United Kingdom)",
      id2: "System admin",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1389017100000],
      off: 0,
    },
    {
      id: "Randor Township School Disctrict:System admin:Inadequate technology",
      id1: "Randor Township School Disctrict",
      id2: "System admin",
      type: "link",
      w: 10,
      d: {
        type: "Inadequate technology",
      },
      dt: [1387207260000],
      off: 0,
      c: "rgba(255, 207, 9, 0.7)",
      fbc: "rgba(0,0,0,0.0)",
    },
    {
      id: "Randor Township School Disctrict:Other:Inadequate technology",
      id1: "Randor Township School Disctrict",
      id2: "Other",
      type: "link",
      w: 10,
      d: {
        type: "Inadequate technology",
      },
      dt: [1387207260000],
      off: 0,
      c: "rgba(255, 207, 9, 0.7)",
      fbc: "rgba(0,0,0,0.0)",
    },
    {
      id: "North Wales Transportation Committee:Activist:Web application",
      id1: "North Wales Transportation Committee",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1387228560000],
      off: 0,
    },
    {
      id: "Turkey Ministry of Food, Agriculture and Livestock:Activist:Web application",
      id1: "Turkey Ministry of Food, Agriculture and Livestock",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1378859280000],
      off: 0,
    },
    {
      id: "Australian Federal Police:Activist:Web application",
      id1: "Australian Federal Police",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1389623100000],
      off: 0,
    },
    {
      id: "Strategic Forecasting, Inc.:Activist:Web application",
      id1: "Strategic Forecasting, Inc.",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1360387488000],
      off: 0,
    },
    {
      id: "Swansea Police Department:Organized crime:Unknown",
      id1: "Swansea Police Department",
      id2: "Organized crime",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1385604420000],
      off: 0,
    },
    {
      id: "Grindr LLC:Unaffiliated:Other",
      id1: "Grindr LLC",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      d: {
        type: "Other",
      },
      dt: [1407771720000],
      off: 0,
      c: "rgba(186, 153, 15, 0.7)",
      fbc: "rgba(0,0,0,0.0)",
    },
    {
      id: "Lewisham Council:End-user:Carelessness",
      id1: "Lewisham Council",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(255, 207, 9, 0.7)",
      d: {
        type: "Carelessness",
      },
      dt: [1372719229000],
      off: 0,
    },
    {
      id: "California State University San Marcos:End-user:Physical access",
      id1: "California State University San Marcos",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1361375898000],
      off: 0,
    },
    {
      id: "Certified Tax Consultants:End-user:LAN access",
      id1: "Certified Tax Consultants",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1386355740000],
      off: 0,
    },
    {
      id: "Hidalgo County:Activist:Web application",
      id1: "Hidalgo County",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1381142400000],
      off: 0,
    },
    {
      id: "Steam, Origin, Battle.net, and League of Legends:Activist:Web application",
      id1: "Steam, Origin, Battle.net, and League of Legends",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1399763731237],
      off: 0,
    },
    {
      id: "Praxair Healthcare Services, Inc.:Former employee:Victim work area",
      id1: "Praxair Healthcare Services, Inc.",
      id2: "Former employee",
      type: "link",
      w: 10,
      c: "rgba(33, 252, 13, 0.7)",
      d: {
        type: "Victim work area",
      },
      dt: [1365805796000],
      off: 0,
    },
    {
      id: "Ameritas Life Insurance Corp.:Unaffiliated:Personal vehicle",
      id1: "Ameritas Life Insurance Corp.",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Personal vehicle",
      },
      dt: [1360041473000],
      off: 0,
    },
    {
      id: "Midlothian Council:End-user:Carelessness",
      id1: "Midlothian Council",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(255, 207, 9, 0.7)",
      d: {
        type: "Carelessness",
      },
      dt: [1372199058000],
      off: 0,
    },
    {
      id: "Massachusetts Technical Institute:Activist:Web application",
      id1: "Massachusetts Technical Institute",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1389545760000],
      off: 0,
    },
    {
      id: "New York City Police Department:Other:LAN access",
      id1: "New York City Police Department",
      id2: "Other",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1377479738000],
      off: 0,
    },
    {
      id: "TL Government of Timor-Leste:Nation-state:Unknown",
      id1: "TL Government of Timor-Leste",
      id2: "Nation-state",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1395155940000],
      off: 0,
    },
    {
      id: "Baylor Law School:End-user:Random error",
      id1: "Baylor Law School",
      id2: "End-user",
      type: "link",
      w: 10,
      d: {
        type: "Random error",
      },
      dt: [1360702744000],
      off: 0,
      c: "rgba(186, 153, 15, 0.7)",
      fbc: "rgba(0,0,0,0.0)",
    },
    {
      id: "Nationalist Movement Website:Activist:Web application",
      id1: "Nationalist Movement Website",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1388877780000],
      off: 0,
    },
    {
      id: "Dream Host:Unaffiliated:Unknown",
      id1: "Dream Host",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1363292897000],
      off: 0,
    },
    {
      id: "L-3 Communications Holdings, Inc.:Other:Victim work area",
      id1: "L-3 Communications Holdings, Inc.",
      id2: "Other",
      type: "link",
      w: 10,
      c: "rgba(33, 252, 13, 0.7)",
      d: {
        type: "Victim work area",
      },
      dt: [1377129826000],
      off: 0,
    },
    {
      id: "KTSU Radio:Other:LAN access",
      id1: "KTSU Radio",
      id2: "Other",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1372464511000],
      off: 0,
    },
    {
      id: "Linode:Activist:Web application",
      id1: "Linode",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1378844012000],
      off: 0,
    },
    {
      id: "BENTON'S ADULT FOSTER CARE:End-user:Physical access",
      id1: "BENTON'S ADULT FOSTER CARE",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1376928229000],
      off: 0,
    },
    {
      id: "Dell Inc:State-affiliated:Unknown",
      id1: "Dell Inc",
      id2: "State-affiliated",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1405087500000],
      off: 0,
    },
    {
      id: "Federal Sentencing Commission:Activist:Web application",
      id1: "Federal Sentencing Commission",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1415824200000],
      off: 0,
    },
    {
      id: "Interactive Data:Unaffiliated:Web application",
      id1: "Interactive Data",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1372797243000],
      off: 0,
    },
    {
      id: "Ha Dinh Primary School:Competitor:Unknown",
      id1: "Ha Dinh Primary School",
      id2: "Competitor",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1378926480000],
      off: 0,
    },
    {
      id: "East Lothian Council:End-user:Carelessness",
      id1: "East Lothian Council",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(255, 207, 9, 0.7)",
      d: {
        type: "Carelessness",
      },
      dt: [1363297806000],
      off: 0,
    },
    {
      id: "Al Arabiya:Activist:Web application",
      id1: "Al Arabiya",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1396535220000],
      off: 0,
    },
    {
      id: "COUNTRYWIDE HOME LOANS, INC.:Finance:LAN access",
      id1: "COUNTRYWIDE HOME LOANS, INC.",
      id2: "Finance",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1391804247000],
      off: -20,
    },
    {
      id: "COUNTRYWIDE HOME LOANS, INC.:Finance:Physical access",
      id1: "COUNTRYWIDE HOME LOANS, INC.",
      id2: "Finance",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1391804247000],
      off: 20,
    },
    {
      id: "HSBC:End-user:Carelessness",
      id1: "HSBC",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(255, 207, 9, 0.7)",
      d: {
        type: "Carelessness",
      },
      dt: [1372722697000],
      off: 20,
    },
    {
      id: "Vodafone:Activist:Web application",
      id1: "Vodafone",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1388441063000],
      off: 0,
    },
    {
      id: "Sony Pictures Entertainment Inc.:Activist:Web application",
      id1: "Sony Pictures Entertainment Inc.",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1392263795000],
      off: 0,
    },
    {
      id: "Texas Department of Health and Human Services:End-user:LAN access",
      id1: "Texas Department of Health and Human Services",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1378844012000],
      off: 20,
    },
    {
      id: "Bank of Nova Scotia:End-user:LAN access",
      id1: "Bank of Nova Scotia",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(252, 132, 39, 0.7)",
      d: {
        type: "LAN access",
      },
      dt: [1405099440000],
      off: 0,
    },
    {
      id: "Wset Incorporated:Organized crime:Email attachment",
      id1: "Wset Incorporated",
      id2: "Organized crime",
      type: "link",
      w: 10,
      c: "rgba(229, 153, 255, 0.7)",
      d: {
        type: "Email attachment",
      },
      dt: [1391197800000],
      off: 0,
    },
    {
      id: "Taco Bell Corp.:Cashier:Physical access",
      id1: "Taco Bell Corp.",
      id2: "Cashier",
      type: "link",
      w: 10,
      c: "rgba(227, 19, 254, 0.7)",
      d: {
        type: "Physical access",
      },
      dt: [1391404682000],
      off: 0,
    },
    {
      id: "Concepta, Inc:Former employee:Unknown",
      id1: "Concepta, Inc",
      id2: "Former employee",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1377201332000],
      off: 0,
    },
    {
      id: "Northumbria Police:Unaffiliated:Unknown",
      id1: "Northumbria Police",
      id2: "Unaffiliated",
      type: "link",
      w: 10,
      c: "rgba(186, 153, 15, 0.7)",
      d: {
        type: "Unknown",
      },
      dt: [1414693500000],
      off: 0,
    },
    {
      id: "Yakima Police Department:End-user:Remote access",
      id1: "Yakima Police Department",
      id2: "End-user",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Remote access",
      },
      dt: [1404070440000],
      off: 0,
    },
    {
      id: "BD Government:Activist:Web application",
      id1: "BD Government",
      id2: "Activist",
      type: "link",
      w: 10,
      c: "rgba(255, 0, 13, 0.7)",
      d: {
        type: "Web application",
      },
      dt: [1388441063000],
      off: 0,
    },
    {
      id: "Bank of the West:Organized crime:Unknown",
      id1: "Bank of the West",
      id2: "Organized crime",
      type: "link",

// ...truncated 55471 lines
<!doctype html>
<html lang="en" style="background-color: #2d383f">
  <head>
    <meta charset="utf-8" />
    <title>Filter Data Breaches</title>
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/fontawesome.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/solid.css"
    />
    <link rel="stylesheet" type="text/css" href="databreaches.css" />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="klchart" class="klchart klchart-timebar"></div>
    <div id="kltimebar" class="kltimebar"></div>
    <script type="module" src="./code.js"></script>
  </body>
</html>
ul.legend {
  margin-top: 14px;
  margin-bottom: 15px;
  list-style: none;
  padding: 0px;
}

ul.legend li {
  height: 30px;
}
ul.legend input {
  margin-top: 9px;
}
ul.legend span.color-legend {
  color: #fff;
  display: inline-block;
  font-weight: bold;
  margin-right: 5px;
  margin-left: 0px;
  padding: 4px;
  width: 28px;
  height: 28px;
  border-radius: 28px;
}

.highlight {
  font-weight: bold;
}

.color-legend {
  margin-left: 5px;
}

#victimName {
  font-size: 14px;
}

.typeahead {
  max-width: 161px;
  min-width: 161px;
  border: 1px solid #ccc;
}

.typeahead li {
  font-size: 14px;
  background-color: transparent;
  padding: 2px 5px;
  width: 100%;
}
.typeahead li a {
  color: #009968;
  width: 100%;
}
.typeahead li:hover a {
  color: #fff;
}
.typeahead li.active a {
  color: #fff;
}
.typeahead li.active {
  background-color: #009968;
}

.popover {
  display: none;
  height: 80px;
  width: 250px;
  font-size: 16px;
  line-height: 16px;
  margin: 0px;
  border: 1px solid #ccc;
  z-index: 1000;
  margin-bottom: -80px;
}

.highlight {
  font-weight: bold;
}

.klchart,
#fullscreen.fullscreenrow .cichart,
#fullscreen.fullscreenrow .klchart {
  border: none;
  background-color: #2d383f;
}

.kltimebar {
  border: none;
  border-top: dashed 1px grey;
  background-color: #2d383f;
}

.popover .popover-title {
  padding: 4px;
  margin: 0px;
  font-size: 16px;
  line-height: 16px;
  width: 100%;
}

.popover .popover-content {
  padding: 4px;
  margin: 0px;
  font-size: 14px;
  line-height: 14px;
  background-color: #fff;
  height: 54px;
  width: 100%;
}

.arrow {
  background-color: #fff;
  border-top: 1px solid #ccc;
  border-right: 1px solid #ccc;
  transform: translateX(244px) translateY(35px) rotateZ(45deg);
  width: 10px;
  height: 10px;
  position: absolute;
}

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.