fluency-v8-components 1.5.9 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ <template>
2
+ <div class="col items-center">
3
+ <div class="text-center">
4
+ <span>{{ title }} Timeline ({{ total }} Events)</span>
5
+ </div>
6
+ <div ref="myplot" :class="['px-4', { 'pb-4': !props.flow }]"></div>
7
+ <div class="flex justify-end w-full mr-2">
8
+ <div ref="myplotLegend"></div>
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script setup>
14
+ import { ref, watch } from "vue";
15
+ import * as d3 from "d3";
16
+ import * as Plot from "@observablehq/plot";
17
+ import { dateLocalTime } from "@/utils/timeUtils";
18
+
19
+ // props
20
+ const props = defineProps({
21
+ title: String,
22
+ buckets: Array,
23
+ total: Number,
24
+ from: Number,
25
+ to: Number,
26
+ flow: {
27
+ type: Boolean,
28
+ default: false,
29
+ },
30
+ width: {
31
+ type: Number,
32
+ default: 1200,
33
+ },
34
+ height: {
35
+ type: Number,
36
+ default: 300,
37
+ },
38
+ theme: {
39
+ type: String,
40
+ default: "light",
41
+ },
42
+ /** Key on each bucket for the risk score value (y-axis). Default "risk_score". */
43
+ valueKey: {
44
+ type: String,
45
+ default: "risk_score",
46
+ },
47
+ });
48
+ // states
49
+ const myplot = ref(null);
50
+ const myplotLegend = ref(null);
51
+ // watch for changes in data
52
+ watch([() => props.buckets], () => {
53
+ plotGraph();
54
+ });
55
+ watch([() => props.theme], () => {
56
+ plotGraph();
57
+ });
58
+
59
+ // function defs
60
+ function plotGraph() {
61
+ if (!props.buckets) return;
62
+ const valueKey = props.valueKey;
63
+ let options = {
64
+ marginTop: 30,
65
+ marks: [
66
+ Plot.frame(),
67
+ Plot.text(["No data available!"], { frameAnchor: "middle" }),
68
+ ],
69
+ };
70
+ let marginLeft = 80;
71
+ let marginRight = 80;
72
+
73
+ if (props.buckets.length > 0) {
74
+ if (props.flow) {
75
+ const b = props.buckets;
76
+
77
+ // Left axis (flow) domain = max of rx/tx
78
+ const leftMax = Math.max(
79
+ 1,
80
+ d3.max(b, (d) => Math.max(d?.rxHistogram?.value ?? 0, d?.txHistogram?.value ?? 0)) ?? 0
81
+ );
82
+
83
+ // Right axis (risk score) domain
84
+ const rightMax = Math.max(
85
+ 1,
86
+ d3.max(b, (d) => d?.[valueKey] ?? 0) ?? 0
87
+ );
88
+
89
+ const k = leftMax / rightMax;
90
+ const rightTicks = d3.ticks(0, rightMax, 5);
91
+ const leftTicks = d3.ticks(0, leftMax, 5);
92
+
93
+ options = {
94
+ x: { type: "time", label: "Time", tickSpacing: 100 },
95
+ y: {
96
+ label: "Flow",
97
+ domain: [0, leftMax],
98
+ },
99
+ grid: true,
100
+ marks: [
101
+ Plot.lineY(b, {
102
+ x: "key",
103
+ y: (d) => d?.rxHistogram?.value ?? 0,
104
+ strokeWidth: 2.5,
105
+ curve: "catmull-rom",
106
+ stroke: "#22c55e",
107
+ }),
108
+ Plot.lineY(b, {
109
+ x: "key",
110
+ y: (d) => d?.txHistogram?.value ?? 0,
111
+ strokeWidth: 2.5,
112
+ curve: "catmull-rom",
113
+ stroke: "#eab308",
114
+ }),
115
+ Plot.dotY(b, {
116
+ x: "key",
117
+ y: (d) => (d?.[valueKey] ?? 0) * k,
118
+ stroke: "#71b6e0",
119
+ strokeWidth: 3,
120
+ }),
121
+ Plot.axisY({
122
+ anchor: "left",
123
+ ticks: leftTicks.map((t) => t),
124
+ tickFormat: (_, i) => d3.format("~s")(leftTicks[i]) + "B",
125
+ label: "Bytes",
126
+ }),
127
+ Plot.axisY({
128
+ anchor: "right",
129
+ ticks: rightTicks.map((t) => t * k),
130
+ tickFormat: (_, i) => d3.format("~s")(rightTicks[i]),
131
+ label: "Risk Score",
132
+ }),
133
+ Plot.tip(
134
+ b,
135
+ Plot.pointerX({
136
+ x: "key",
137
+ y: (d) => (d?.[valueKey] ?? 0) * k,
138
+ fill: props.theme === "light" ? "white" : "black",
139
+ title: (d) =>
140
+ `Time: ${dateLocalTime(d.key)}\n` +
141
+ `rx: ${d?.rxHistogram?.value ?? 0}B (~${d3.format(".0s")(d?.rxHistogram?.value)}B)\n` +
142
+ `tx: ${d?.txHistogram?.value ?? 0}B (~${d3.format(".0s")(d?.txHistogram?.value)}B)\n` +
143
+ `Risk Score: ${Math.round(d?.[valueKey] ?? 0)}`,
144
+ })
145
+ ),
146
+ ],
147
+ };
148
+ } else {
149
+ const dataMax = d3.max(props.buckets, (d) => d?.[valueKey] ?? 0) ?? 0;
150
+ const maxRisk = dataMax > 0 ? Math.max(1, dataMax) : 500;
151
+ const allZeros = dataMax === 0;
152
+ const tickFormat = d3.format("d");
153
+ const widestTickLabel = tickFormat(maxRisk).toString();
154
+ marginLeft = Math.max(marginLeft, 24 + widestTickLabel.length * 8);
155
+
156
+ options = {
157
+ x: {
158
+ type: "time",
159
+ label: "Time",
160
+ tickSpacing: 100,
161
+ },
162
+ y: {
163
+ label: "Risk Score",
164
+ domain: [0, maxRisk],
165
+ tickFormat,
166
+ ...(allZeros && { ticks: [0, 100, 200, 300, 400, 500] }),
167
+ },
168
+ grid: true,
169
+ marks: [
170
+ Plot.dotY(props.buckets, {
171
+ x: "key",
172
+ y: (d) => d?.[valueKey] ?? 0,
173
+ stroke: "#71b6e0",
174
+ strokeWidth: 3,
175
+ }),
176
+ Plot.tip(
177
+ props.buckets,
178
+ Plot.pointerX({
179
+ x: "key",
180
+ y: (d) => d?.[valueKey] ?? 0,
181
+ fill: props.theme === "light" ? "white" : "black",
182
+ title: (d) =>
183
+ `Time: ${dateLocalTime(d.key)}\nRisk Score: ${Math.round(d?.[valueKey] ?? 0)}`,
184
+ })
185
+ ),
186
+ ],
187
+ };
188
+ }
189
+ }
190
+
191
+ const plot = Plot.plot({
192
+ marginLeft,
193
+ marginRight,
194
+ width: props.width,
195
+ height: props.height,
196
+ marginBottom: 50,
197
+ inset: 20,
198
+ style: {
199
+ fontSize: "16px",
200
+ backgroundColor: props.theme === "light" ? "white" : "#1a202c",
201
+ },
202
+ ...options,
203
+ });
204
+ myplot.value.innerHTML = "";
205
+ myplot.value.appendChild(plot);
206
+ if (props.flow) {
207
+ const legend = Plot.legend({
208
+ color: {
209
+ type: "categorical",
210
+ domain: ["Received", "Sent", "Risk Score"],
211
+ range: ["#22c55e", "#eab308", "#71b6e0"],
212
+ },
213
+ swatchSize: 8,
214
+ });
215
+ myplotLegend.value.innerHTML = "";
216
+ myplotLegend.value.appendChild(legend);
217
+ }
218
+ }
219
+ </script>
@@ -9,9 +9,11 @@
9
9
  {{ parent.title }}
10
10
  <span class="font-light">({{ children.length }})</span>
11
11
  </div>
12
- <div class="flex">
12
+ <div class="flex pr-1">
13
13
  <PlusCircleIcon class="icon-lg" :class="noMore()" @click="topUp()" />
14
14
  <MinusCircleIcon class="icon-lg" :class="noLess()" @click="topDown()" />
15
+ <ArrowDownCircleIcon v-if="descending" class="icon-lg std-blue-text cursor-pointer" @click="sortData" />
16
+ <ArrowUpCircleIcon v-else class="icon-lg std-blue-text cursor-pointer" @click="sortData" />
15
17
  </div>
16
18
  <Tooltip
17
19
  v-if="props.tooltip"
@@ -22,7 +24,7 @@
22
24
  />
23
25
  </div>
24
26
  <div
25
- v-for="child in children.slice(0, topLevel)"
27
+ v-for="child in filtered"
26
28
  class="flex justify-between py-1"
27
29
  >
28
30
  <div class="flex items-center min-w-0">
@@ -40,11 +42,12 @@
40
42
  </template>
41
43
 
42
44
  <script lang="ts" setup>
43
- import { ref } from "vue";
44
- import { PlusCircleIcon, MinusCircleIcon } from "@heroicons/vue/20/solid";
45
+ import { ref, computed } from "vue";
46
+ import { PlusCircleIcon, MinusCircleIcon, ArrowUpCircleIcon, ArrowDownCircleIcon } from "@heroicons/vue/20/solid";
45
47
  import Tooltip from "../Tooltip.vue";
46
48
  import TriState from "./TriState.vue";
47
49
 
50
+ // props and emits
48
51
  const props = defineProps({
49
52
  parent: {
50
53
  type: Object,
@@ -62,9 +65,21 @@ const props = defineProps({
62
65
  },
63
66
  });
64
67
  const emits = defineEmits(["newState"]);
68
+ // states
65
69
  const topLevel = ref(props.parent.show);
66
70
  const tooltip = ref(false);
71
+ const descending = ref(true);
72
+ // computed
73
+ const filtered = computed(() => {
74
+ const copy = JSON.parse(JSON.stringify(props.children))
75
+ if (!descending.value) {
76
+ copy.reverse();
77
+ }
78
+ return copy.slice(0, topLevel.value);
79
+ });
80
+
67
81
 
82
+ // function defs
68
83
  function noMore() {
69
84
  if (topLevel.value < props.children.length) {
70
85
  return "std-blue-text cursor-pointer";
@@ -91,6 +106,10 @@ function topDown() {
91
106
  }
92
107
  }
93
108
 
109
+ function sortData() {
110
+ descending.value = !descending.value
111
+ }
112
+
94
113
  function updateParent(parent: string, child: string, newState: string) {
95
114
  emits("newState", parent, child, newState);
96
115
  }
@@ -119,6 +119,8 @@ export { default as StackedChartClustered } from "./charts/StackedChartClustered
119
119
  export { default as PieChart } from "./charts/PieChart.vue";
120
120
  export { default as ProgressChart } from "./charts/ProgressChart.vue";
121
121
  export { default as TimelineChart } from "./charts/TimelineChart.vue";
122
+ export { default as TimelinePlot } from "./charts/TimelinePlot.vue";
123
+ export { default as RangeSlider } from "./charts/RangeSlider.vue"
122
124
  export { default as EmptyChart } from "./charts/EmptyChart.vue";
123
125
  export { default as WorkflowChart } from "./charts/WorkflowChart.vue";
124
126
 
@@ -20,7 +20,7 @@
20
20
  <div class="grid grid-cols-3 md:grid-cols-4">
21
21
  <div
22
22
  :class="[
23
- 'w-full col-span-3 md:col md:col-span-1 md:overflow-auto',
23
+ 'w-full col-span-3 md:col md:col-span-1 md:overflow-auto custom-scrollbar',
24
24
  { 'md:max-h-[calc(100vh-255px)]': searchBar },
25
25
  { 'md:max-h-[calc(100vh-200px)]': !searchBar },
26
26
  ]"
@@ -29,7 +29,7 @@
29
29
  </div>
30
30
  <div
31
31
  :class="[
32
- 'col-span-3 md:ml-5 md:overflow-auto',
32
+ 'col-span-3 md:ml-5 md:overflow-auto custom-scrollbar',
33
33
  { 'md:max-h-[calc(100vh-255px)]': searchBar },
34
34
  { 'md:max-h-[calc(100vh-200px)]': !searchBar },
35
35
  ]"
@@ -1,7 +1,7 @@
1
1
  import Prism from "prismjs";
2
2
 
3
3
  const builtinfunction =
4
- /#?(?!\s)\b(?:Add|After|Aggregate|alert|anomaly|append|Append|AWS_AccountRegionLambda|AWS_AccountLambda|AWS_GetCostUsage|AWS_GetMetric|AWS_GetPrice|AWS_LoadAsset|base64Encode|base64Decode|Before|Clone|coalesce|ColumnAggregate|concat|contains|content|Cylance_AddGlobalList|Cylance_GetDevice|Cylance_GetThreatDevices|Cylance_GetThreatDownload|Cylance_GetThreatInfo|Cylance_LoadDevice|Cylance_LoadThreat|decoder_CEF|decoder_CSV|decoder_MixedKeyValue|decoder_QuotedKeyValue|DimensionTable|Each|Emit|endsWith|Error|Find|Filter|Fluency_BehaviorSearch|Fluency_Device_Add|Fluency_Device_Delete|Fluency_DeviceSearch|Fluency_Device_Lookup|Fluency_Device_LookupName|Fluency_Device_Update|Fluency_EntityinfoLookup|Fluency_FusionEvent|fluencyLavadbFpl|Fluency_LavadbQuery|Fluency_LavaLakeFpl|Fluency_ResourceLoad|Fluency_SummarySearch|Fluency_Tenant_Device_Add|Fluency_Tenant_Device_Delete|Fluency_Tenant_Device_Lookup|Fluency_Tenant_Device_LookupName|Fluency_Tenant_Device_Update|Format|geoip|GetColumnValues|GetEnv|GetKeys|GetRow|gzipCompress|gzipDecompress|htmlTemplate|indexOf|isEmpty|IsEmpty|isIPv4|isIPv6|ipNormalize|isNull|isNumber|isString|isUndef|isValidIP|Join|JoinStream|jsonClone|jsonTable|len|Limit|main|Map|match|mergeTable|NewColumns|NewColumnLambda|parseBool|parseFloat|parseInt|parseJson|Platform_Action|Platform_Action_Endpoint|Platform_Asset_Lookup|Platform_Asset_Refresh|Platform_Asset_Register|Platform_Cache_Check|Platform_Cache_Delete|Platform_Cache_DeRegister|Platform_Cache_Get|Platform_Cache_Register|Platform_Cache_Replace|Platform_Cache_Set|Platform_Cache_SetMultiple|Platform_Channel|Platform_DataObject_Delete|Platform_DataObject_Get|Platform_DataObject_Save|Platform_DataObject_Search|Platform_EntityinfoCheck|Platform_EntityinfoLookup|Platform_EntityProvider_Lookup|Platform_EntityProvider_Refresh|Platform_Grok_Add_Pattern|Platform_Grok_Check|Platform_Grok_Parse|Platform_Grok_Register|Platform_LoadComponent|Platform_Metric_Alert_Counter_Stop|Platform_Metric_Counter|Platform_Metric_Query|Platform_Metric_QueryBuild|Platform_Metric_QueryRange|Platform_Metric_Sort|Platform_Metric_Sort_Histogram|Platform_Notification_Email|Platform_Notification_PagerDuty|Platform_Notification_Slack|Platform_Notification_ServiceNow|Platform_PluginLambda|Platform_REST_Call|Platform_Sink|Platform_Site_GetInfo|Platform_Site_GetTenants|Platform_Site_LoadTenantComponent|pluginLambda|Plugin_Bitdefender_LoadEndpoint|Plugin_Falcon_GetHost|Plugin_Falcon_GetIncident|Plugin_Falcon_LoadHost|Plugin_Falcon_LoadIncident|Plugin_InControl_LoadClient|Plugin_InControl_LoadDevice|printf|Prom_Push_Counter|Prom_Push_Gauge|regexp|replace|replaceAll|ReplaceKey|RenameColumn|RemoveColumn|Round|run|RxFPL_GetMetric|setEnv|split|Some|Sort|sprintf|startsWith|SetColumnUnit|SetDimensions|SetTags|SetUnit|sleep|SummaryTable|subString|template|TimeTable|toLower|timezoneOffset|toUpper|transform|trim|trimPrefix|trimSuffix|typeof|Unix|UnixMilli|window)\b(?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/g;
4
+ /#?(?!\s)\b(?:Add|After|Aggregate|alert|anomaly|append|Append|AWS_AccountRegionLambda|AWS_AccountLambda|AWS_GetCostUsage|AWS_GetMetric|AWS_GetPrice|AWS_LoadAsset|base64Encode|base64Decode|Before|Clone|coalesce|ColumnAggregate|concat|contains|content|Cylance_AddGlobalList|Cylance_GetDevice|Cylance_GetThreatDevices|Cylance_GetThreatDownload|Cylance_GetThreatInfo|Cylance_LoadDevice|Cylance_LoadThreat|decoder_CEF|decoder_CSV|decoder_MixedKeyValue|decoder_QuotedKeyValue|DimensionTable|Each|Emit|endsWith|Error|Find|Filter|Fluency_BehaviorSearch|Fluency_Device_Add|Fluency_Device_Delete|Fluency_DeviceSearch|Fluency_Device_Lookup|Fluency_Device_LookupName|Fluency_Device_Update|Fluency_EntityinfoLookup|Fluency_FusionEvent|fluencyLavadbFpl|Fluency_LavadbQuery|fluencyLavaLakeFpl|Fluency_LavaLakeFpl|Fluency_ResourceLoad|Fluency_SummarySearch|Fluency_Tenant_Device_Add|Fluency_Tenant_Device_Delete|Fluency_Tenant_Device_Lookup|Fluency_Tenant_Device_LookupName|Fluency_Tenant_Device_Update|Format|geoip|GetColumnValues|GetEnv|GetKeys|GetRow|gzipCompress|gzipDecompress|htmlTemplate|indexOf|isEmpty|IsEmpty|isIPv4|isIPv6|ipNormalize|isNull|isNumber|isString|isUndef|isValidIP|Join|JoinStream|jsonClone|jsonTable|len|Limit|main|Map|match|mergeTable|NewColumns|NewColumnLambda|parseBool|parseFloat|parseInt|parseJson|Platform_Action|Platform_Action_Endpoint|Platform_Asset_Lookup|Platform_Asset_Refresh|Platform_Asset_Register|Platform_Cache_Check|Platform_Cache_Delete|Platform_Cache_DeRegister|Platform_Cache_Get|Platform_Cache_Register|Platform_Cache_Replace|Platform_Cache_Set|Platform_Cache_SetMultiple|Platform_Channel|Platform_DataObject_Delete|Platform_DataObject_Get|Platform_DataObject_Save|Platform_DataObject_Search|Platform_EntityinfoCheck|Platform_EntityinfoLookup|Platform_EntityProvider_Lookup|Platform_EntityProvider_Refresh|Platform_Grok_Add_Pattern|Platform_Grok_Check|Platform_Grok_Parse|Platform_Grok_Register|Platform_LoadComponent|Platform_Metric_Alert_Counter_Stop|Platform_Metric_Counter|Platform_Metric_Query|Platform_Metric_QueryBuild|Platform_Metric_QueryRange|Platform_Metric_Sort|Platform_Metric_Sort_Histogram|Platform_Notification_Email|Platform_Notification_PagerDuty|Platform_Notification_Slack|Platform_Notification_ServiceNow|Platform_PluginLambda|Platform_REST_Call|Platform_Sink|Platform_Site_GetInfo|Platform_Site_GetTenants|Platform_Site_LoadTenantComponent|pluginLambda|Plugin_Bitdefender_LoadEndpoint|Plugin_Falcon_GetHost|Plugin_Falcon_GetIncident|Plugin_Falcon_LoadHost|Plugin_Falcon_LoadIncident|Plugin_InControl_LoadClient|Plugin_InControl_LoadDevice|printf|Prom_Push_Counter|Prom_Push_Gauge|regexp|replace|replaceAll|ReplaceKey|RenameColumn|RemoveColumn|Round|run|RxFPL_GetMetric|setEnv|split|Some|Sort|sprintf|startsWith|SetColumnUnit|SetDimensions|SetTags|SetUnit|sleep|SummaryTable|subString|template|TimeTable|toLower|timezoneOffset|toUpper|transform|trim|trimPrefix|trimSuffix|typeof|Unix|UnixMilli|window)\b(?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/g;
5
5
 
6
6
  const fpl2 = {
7
7
  regex: Prism.languages.javascript.regex,