跳转到内容

简化 ​

许多图表大多是重复的。两个事件共享四十个 IP,每个 IP 需要一个节点、一个标签和两条边,尽管重要的是“这两个事件共享四十个 IP”。一个枢纽有几十个叶子,与其他地方没有联系。 Simplify 将扮演相同角色的节点折叠成一个 group,并在其位置绘制:

ts
const options = {
    render: {
        nodeTypeAccessor: (node) => node.getData().type,
    },
    UI: {
        simplify: {
            rules: [{ kind: 'neighbours', minSize: 5 }],
        },
    },
}

组是视图,而不是数据

graph.getNodes()、getEdges()、表、导出和撤消历史记录看到真实节点。每当图表发生变化时,就会根据它们重新计算一个组,并且不会保存任何内容。

查看它在 简化图表 卡上运行。

规则 ​

UI.simplify.rules 是一个有序列表。每个规则给一些节点一个密钥,共享密钥的节点成为一组,只要至少有minSize个。

  • 规则按照的顺序运行,每个规则都将其上面的规则所创建的组视为普通节点。因此,一个组可以通过稍后的规则再次分组。
  • 规则在节点过滤器之后运行,因此组仅保留通过它们的内容:隐藏四十个IP中的三个,组读取37。边缘层不会更改组;关闭其中之一只会隐藏线条。
  • 附有注释的节点和扩展集群是从不分组。封闭的集群与任何其他节点一样分组。
  • 除非显示 enabled: false,否则声明的规则已启用。

没有UI.simplify,full模式提供在简化轨道模式下关闭的内置规则;其他模式什么都不运行。 UI.simplify.enabled: false 删除该功能。

邻居规则 ​

ts
{ kind: 'neighbours', minSize: 5, typeOf: (node) => node.getData().type }

它折叠链接到 ** 的一种类型的节点,其方式与**相同:指向它们的相同节点以及它们指向的相同节点。轮毂的叶子是最简单的情况。由于事件 A 和 B 共享三个 TTP,并且每个 TTP 都有自己的两个,因此您将获得三个组:A 和 B 之间共享的三个 TTP,以及每侧两个。

  • 类型是规则自己的typeOf,否则为UI.simplify.typeOf,否则为render.nodeTypeAccessor。没有类型组的节点。
  • 没有任何链接的节点永远不会被分组:一个组需要一些东西来挂起。
  • 获得链接的节点离开其组,前往与其新邻居匹配的节点。如果剩余的数量少于minSize,则该团体解散。
  • 组在保留其大部分成员的同时保留其身份,因此具有相同邻居的新节点会加入现有组而不移动它。

链式法则 ​

ts
{ kind: 'chains', minSize: 5, typeOf: (node) => node.getData().type }

它折叠悬挂在一个节点上的链条。包含三十个文件的事件,每个文件都有自己的哈希值,是三十个链file > sha256:文件不共享邻居,因为每个文件都链接自己的哈希值,因此邻居规则不会影响它们。链式法则将它们折叠成一组链接到一组散列的文件,并将它们之间的三十个链接绘制为一条线。

  • 节点的tail是唯一链接来自该节点的节点,顺流而下,而每一步仅沿着链链接。一个节点与另外两个节点、一个分支或一个循环相连,链就在那里结束。
  • 当引导相同形状的链时,当它们共享相同的其他链接和类型时,它们会折叠在一起。五个隔离对 domain → ip 给出一组链接到一组 5 个 IP 的 5 个域。
  • 每层的链条也会折叠:在他们的头组下,或者当没有匹配时在他们自己的头下。
  • 链接计数仅下游:一个节点唯一指向另一个节点的链接不是该节点的尾部。
  • 放置在邻居规则之后,它会折叠该规则留下的内容。邻居规则创建的组可以是尾部。
ts
{ kind: 'degree', minDegree: 2 }
{ kind: 'kcore', k: 2 }

这些规则根据节点的链接程度将节点从画布中取出,并将它们折叠到它们悬挂的节点中,以便计数保留在画布上:

  • 很少的链接 (degree) 折叠少于minDegree链接的节点:在 2 处,集线器的叶子成为集线器上的一组。
  • 在核心之外(kcore)剥离少于k链接的节点,然后对剩下的再次执行此操作,直到每个站立的节点都具有k链接。当值为 2 时,它会折叠悬挂在图表上的整棵树,而不仅仅是它们的叶子。

接触相同节点的折叠节点共享一个组,无论其类型如何,并且一个组可以包含单个节点:它在其锚点上读取“1 more”。连接在一起的折叠节点进入同一组,因此悬挂在集线器上的路径会与集线器的叶子折叠。一连串没有接触到任何站立的折叠节点是它自己的一组,并且每个没有连接到任何东西的节点共享一个组。两个设置的范围都是从 1 到 10,并且链接都以任意方式计数。

在邻居规则之后,一组是这些规则的一个节点:它的链接就是它的线。一群孤立无援的人保持原样。

社区 ​

ts
{ kind: 'communities', level: 4 }

对于对于上述规则而言太大的图,社区会折叠整个邻域:彼此链接比与图的其余部分更紧密地链接的节点成为一组,无论其类型如何。它们是用莱顿算法找到的,对于同一张图每次都是相同的。

  • level从1,细,许多小社区,到7,粗,少数大社区。在简化模式下,它是一个滑块,当您放开时应用。
  • 这些社区在工作人员中被发现,在页面之外,只有当图表的形状发生变化时才会再次出现。同时,卡片上写着分组…,并且之前的组保留。如果没有工作人员可以启动,例如内容安全策略会阻止 blob 工作人员,则相同的代码会在页面上运行; useWorker: false要求这样做。
  • 对于每条规则,一个节点的社区仍然是一个节点,而带注释的节点则不存在。
  • 与其他内置规则不同,社区仅在声明时处于简化模式。

拓展着陆 ​

ts
{ kind: 'landings', minSize: 2 }

以组形式摄取,在pivot查看窗格的页脚中,会将选定的行折叠起来:每种类型一个组,挂在您旋转的节点上。四十个 IP 以“40 × ip”形式到达,而不是四十个点。

  • 一个组持有所摄取的添加的。画布上已有的节点将保留在原来的位置。
  • 与邻居规则不同,成员保留以后链接到的任何内容。将其拉出、删除或撤消摄取以将其取出。撤消会解散该组,重做会将其恢复。
  • 节点数少于 minSize(默认为 2)的类型将作为普通节点。
  • 该规则首先运行,因此它之后的规则将每个着陆视为一个节点。您不需要声明它:第一个分组着陆将其添加到“简化”模式的顶部,打开。声明它以将其放在其他地方或更改其最小组。
  • 哪些着陆被分组为会话的最后,如开放组。
  • 登陆组的 landing 名称来自于:{ runId, pivotId, pivotLabel },带有来自拓展定义的标签。芯片无需解析运行 ID 即可读取12 × ip · Event elements。

要对每个拓展结果进行分组,请设置 pivotIngestGrouped。一键运行和autoIngest拓展,永远不会打开评论,然后也进行分组:

js
new Pivotick(el, data, { pivotIngestGrouped: true })

评论的页脚遵循以下选项:

pivotIngestGrouped摄取所选和摄取全部第二个按钮
关闭(默认)土地松动集体摄取
上土地分组松散摄取

当 UI.simplify 关闭时,该选项不会执行任何操作,也没有第二个按钮。

要从代码中选择一次摄取,请传递 group:

js
await graph.pivots.ingest('correlations', 'triage', { group: true })

自定义规则 ​

您自己的规则是 partition(view) 返回每个节点的密钥:

ts
{
    kind: 'custom',
    id: 'bySensor',
    label: 'By sensor',                      // its name in the Simplify mode
    description: 'Sightings reported by the same sensor.',
    minSize: 2,                              // the default for a custom rule
    partition: (view) => new Map(view.nodes
        .filter((node) => view.typeOf(node) === 'sighting')
        .map((node) => [node.id, node.getData().sensor])),
}

view 是这条规则所看到的图形:

会员它是什么
nodes此规则可能分组的节点:通过过滤器的节点,用早期规则的组代替其成员。带注释的节点、拉出的节点和扩展的簇都被省略。
inNeighbours(node) / outNeighbours(node)绘制的节点带有一条通往或来自该节点的线。
typeOf(node)规则分组的类型:UI.simplify.typeOf,否则render.nodeTypeAccessor。
groupOf(node)节点后面的组(当它是较早制定的规则时)。

地图之外的节点将保留其自身,并且不在 view.nodes 中的 id 将被忽略。抛出的规则被关闭,其处于简化模式的卡如此说明,并且其他规则仍然运行。

简化模式 ​

导轨上的 Simplify 按钮会为每个规则打开一张卡片,按运行顺序排列:开关、其设置以及规则折叠的内容。该设置是用于最小组、最少链接或核心强度的 - / + 步进器,以及用于社区级别的滑块。应用程序声明的规则带有 app 标签。顶部的摘要读取画布上有多少个节点通过了过滤器,并且轨道按钮在任何规则折叠时对组进行计数。

与团队合作 ​

一个组被绘制为一个节点,并且只要有明确的地方,它就为其成员起作用。

  • 双击打开,Enter,侧边栏或上下文菜单。它的成员以普通节点的身份回归,在团体颜色的洗礼中,轻轻一拉就能将他们聚集在一起。拖动它们上方的筹码即可将它们全部移动;它的×将它们折叠起来。它的工具提示命名了创建该组的规则。成员们回到他们坐的地方;那些没有自己自由位置的人散布在群体周围,而现场物理会受到温和的推动,就像集群一样。 simulation.fitViewOnExpandCollapse 适合打开和关闭时的视图。成员数量超过 UI.simplify.openConfirmAbove (100) 的小组首先提出请求。
  • 选择即可在侧边栏中的可排序列表中查看其成员。 拉出将成员保留在组之外,直到将其从其上下文菜单放回:在组打开时在画布上,或者从数据停靠中的行(无论组是否打开)放回。通过选择,它会提取出每个选定的成员。
  • 在侧边栏或上下文菜单中的表中查看列出了数据坞中的成员,其中列过滤器进一步缩小了范围。桌子上方的芯片再次列出了每个节点。
  • 选择成员、透视和删除作用于成员。 Pivot 和Delete 会经历通常的钩子,因此onBeforeDelete 会看到真实的节点。隐藏、删除和旋转所选内容,使组也对其成员起作用。
  • 选择一个组会点亮它链接到的节点,就像选择一个节点会点亮其邻居一样。组不提供编辑和连接:它没有自己的数据。

打开组和拉出的节点将持续到会话中,并且不是撤消步骤。要恢复它们,请调用 graph.simplify.open() 和 pullOut()。

手工制作的组 ​

选择两个或更多节点,然后在侧边栏批量栏中选择 Group 或在所选节点的上下文菜单中对所选节点进行 * 分组*。输入标题,或保留提供的标题(“3 × ip,2 × 域名”),所选内容将折叠到该标题下的一组中。

  • 手工制作的组获胜。其规则手工制作先于其他规则运行,因此选中的节点会离开任何规则组。选定的组给出其成员,选择到第二个手工制作组的节点则离开第一个组。带注释的节点和开放簇不会出现,提示会显示有多少个。
  • 它保留其成员链接到的任何内容。被删除或过滤掉的则不绘制;留下一名成员的小组根本不会被抽签,并且会带着其成员回来。
  • 从侧边栏面板或其上下文菜单中重命名和取消组合。空标题再次按其类型对其进行标记。批量栏的取消分组删除选择保留或位于其中的每个手工制作的组。
  • 分组、重命名和取消分组是撤消步骤,与打开或拉出不同。
  • 它是视图状态:未导出,并且重新加载时消失。要保留它,请保存getManualGroups()并将其交还给setManualGroups()。在new Graph()之后恢复、打开或拉出的组是开放布局的一部分:封闭的组被放置为一个点,开放的组的成员保持在一起。
js
const id = graph.simplify.groupNodes(['ip-1', 'ip-2', 'dom-7'], 'Suspicious infra')
graph.simplify.renameGroup(id, 'Cleared infra')
graph.simplify.ungroup(id)

搜寻中 ​

搜索会遍历每个节点,包括折叠的节点。

  • 组内的匹配在其结果行中如此表示:in 12 × ip。
  • 选择它会打开组并选择节点,就像 选择成员 一样。成员数量超过 openConfirmAbove 的组将保持关闭状态并被选择。
  • 在画布上显示全部,结果的最后一行或Shift+Enter,关闭搜索并点亮每个匹配项;其他一切都会消失。一群拿着火柴的人也被点亮,圆环上的主题颜色弧线显示了他们的份额。它的工具提示对它们进行了计数:2 / 12 匹配“198.51”。 Esc,单击空白画布或下一个搜索结束。

将图例条目悬停以相同的方式标记组:持有该条目节点的组被点亮,它们在其环上共享,因此混合类型的组不会读作全部。

具有自己搜索功能的应用程序可以使用 graph.simplify.setMatches(nodes, query) 以相同的方式标记其匹配项,并使用 setMatches([]) 清除它们。

外观 ​

组是围绕圆盘的一个环,其类型的颜色与圆盘的颜色相同,一旦有空间,圆盘上就会显示计数,并且下面有一个标签。它随着数量的增加而增长,直至达到极限。一组混合类型有一个中性盘和一个按类型分开的环。

默认标签为12 × ip。 typeLabel 命名你的类型:

ts
UI: {
    simplify: {
        typeLabel: (type, count) => `${count} ${count === 1 ? type : `${type}s`}`,
    },
}

render.groupStyle(group) 返回在默认值之上分层的 NodeStyle,因此 tiers、html、shape 和徽章在组上工作就像在节点上一样。 render.groupOutline(group) 设置开放组上的芯片的内容:

ts
render: {
    groupStyle: (group) => group.rule === 'bySensor' ? { color: '#6b7280' } : undefined,
    groupOutline: (group) => `${group.members.length} from ${group.rule}`,
}

groupStyle 还接收 base,即库绘制的样式。您返回的内容会替换其密钥,因此添加层的主机会扩展 base.tiers 以保留光盘上的计数:

ts
render: {
    groupStyle: (group, base) => ({
        tiers: [...(base.tiers ?? []), { width: 140, height: 44, style: { html: () => chip(group) } }],
    }),
}

每个人都会收到一个GroupInfo:

领域它是什么
id团体保持稳定,同时保留大部分成员。
rule'neighbours'、'chains'、'degree'、'kcore'、'communities'、'landings'、'manual' 或自定义规则的 id。
members它代表的真实节点。
typeCounts每种类型有多少成员; '' 不是类型。
anchors它链接到的绘制节点,包括组。
open其成员是否回到画布上。
level发现它的社区级别。
title手工制作组的标题。
landing登陆组的跑动和旋转:{ runId, pivotId, pivotLabel }。

组的工具提示为其规则命名,列出其链接到的内容,对于混合组,还按类型列出其成员。 tooltip.renderGroupExtra(group, ctx) 添加您自己的线,就像 renderNodeExtra 对于节点所做的那样。组会跳过为节点数据写入的tooltip.render和renderNodeExtra。

主机的类型和颜色 ​

小组按 render.nodeTypeAccessor 计算其零件,并根据每个成员的风格颜色对它们进行着色。两个 UI.simplify 选项可以更改每个规则和组的设置,而无需触及样式、图例或过滤器:

  • typeOf(node) 是每个规则分组所依据的类型,并且每个组都根据其部件进行计数:标签、混合环的份额、手工制作的组的标题。规则自己的 typeOf 仍然会赢得该规则。
  • colorOf(node) 是节点在组中代表的颜色:圆盘和环、开放式洗涤、工具提示的碎片。返回 undefined 会退回到节点的样式颜色。一个小组自己的groupStyle仍然对封闭的图纸有最后的决定权。

按元素设置样式、按子类型分组并自行绘制节点的主机:

ts
const options = {
    render: {
        nodeTypeAccessor: (node) => node.getData().element,
        nodeStyleMap: { attribute: { shape: 'none', html: drawAttribute } },
    },
    UI: {
        simplify: {
            typeOf: (node) => `${node.getData().element}:${node.getData().subtype}`,
            colorOf: (node) => palette[node.getData().subtype],
        },
    },
}

表中 ​

数据坞列出了真实的节点。折叠节点在 Visibility 列中读取 grouped,并且 Group 列在存在组时为其命名。一条线绘制到一组的边也读取 grouped。

运行时 ​

graph.simplify 保存规则和组:

方法它的作用
setRules(rules) / getRules()更换规则;阅读每个人的状态以及折叠的内容。
setRuleEnabled(id, on) / setRuleSetting(id, n)简化模式的开关和步进器的作用。 setRuleMinSize(id, n) 仅设置最小组。
getGroups() / groupOf(node)每个组以及节点被折叠到的第一组。稍后规则再次折叠的组也会在持有该组的旁边列出,并且都列出其成员; summary().groups 计算画布绘制的封闭组。
open(group) / close(group)将小组成员放在画布上,或将其折叠起来。
pullOut(node) / putBack(node) / isPulledOut(node)将节点保留在任何组之外,或者将其放回。
groupNodes(nodes, title?) / renameGroup(group, title) / ungroup(groups) / isManual(group)手动制作、重新命名或删除组;每个都是撤消步骤。
getManualGroups() / setManualGroups(list)保存手工制作的组并恢复它们。
groupLanding(runId) / ungroupLanding(runId) / isLandingGrouped(runId)将拓展运行添加的内容折叠到着陆组中,或者再次将其松开。
summary()绘制多少个节点,通过过滤器,然后折叠。
setMatches(nodes, query) / matchesIn(group)在包含搜索的组上标记搜索的匹配项;读取一组拥有多少个。
onChange(listener)分组变化后调用;返回其取消订阅。