Skip to content

Commit 5d69229

Browse files
committed
Fix logic, event listener leak, and performance bugs in userscript
1 parent 9a5027e commit 5d69229

2 files changed

Lines changed: 31 additions & 21 deletions

File tree

s1_powerquery_hunting.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{
2929
"category": "Lateral Movement",
3030
"name": "ATExec was used",
31-
"query": "indicator.name contains \"ScheduleTaskRegister\"\n| let taskCode = indicator.metadata.extract_matches('Task: \"\\\\\\\\([A-Za-z0-9]{1,8})\"').get(0)\n| filter taskCode != null\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count = count() by endpoint.name, src.process.user, src.process.storyline.id, taskCode, indicator.metadata\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.storyline.id, taskCode, indicator.metadata, Count\n| sort -Count\n| limit 100000"
31+
"query": "indicator.name contains \"ScheduleTaskRegister\"\n| let taskCode = indicator.metadata.extract_matches('Task: \"\\\\\\\\([A-Za-z0-9_\\-]{1,64})\"').get(0)\n| filter taskCode != null\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count = count() by endpoint.name, src.process.user, src.process.storyline.id, taskCode, indicator.metadata\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.storyline.id, taskCode, indicator.metadata, Count\n| sort -Count\n| limit 100000"
3232
},
3333
{
3434
"category": "Credential Access",
@@ -38,7 +38,7 @@
3838
{
3939
"category": "Lateral Movement",
4040
"name": "Possible WMIC lateralisation",
41-
"query": "(( src.process.name contains:anycase (\"cmd.exe\",\"powershell.exe\") OR tgt.process.name contains:anycase (\"cmd.exe\",\"powershell.exe\")) AND src.process.cmdline contains:anycase \"2\" AND src.process.cmdline contains:anycase \"&1\" AND src.process.cmdline In contains:anycase ( \"C$\",\"ADMIN$\",\"IPC$\",\"PRINT$\",\"FAX$\",\"Temp\") AND src.process.cmdline OR src.process.cmdline contains:anycase \"localhost\" OR src.process.cmdline contains:anycase \"WIndows\") and osSrc.process.name contains \"wmiprvse.exe\"\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, tgt.process.name, tgt.process.cmdline\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, tgt.process.name, tgt.process.cmdline, Count\n| sort -Count\n| limit 100000\n"
41+
"query": "(( src.process.name contains:anycase (\"cmd.exe\",\"powershell.exe\") OR tgt.process.name contains:anycase (\"cmd.exe\",\"powershell.exe\")) AND src.process.cmdline contains:anycase \"2\" AND src.process.cmdline contains:anycase \"&1\" AND (src.process.cmdline contains:anycase (\"C$\",\"ADMIN$\",\"IPC$\",\"PRINT$\",\"FAX$\",\"Temp\") OR src.process.cmdline contains:anycase \"localhost\" OR src.process.cmdline contains:anycase \"Windows\") AND osSrc.process.name contains \"wmiprvse.exe\")\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, tgt.process.name, tgt.process.cmdline\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, tgt.process.name, tgt.process.cmdline, Count\n| sort -Count\n| limit 100000\n"
4242
},
4343
{
4444
"category": "Execution & LOLBAS",
@@ -63,7 +63,7 @@
6363
{
6464
"category": "Command & Control",
6565
"name": "LOLTunnels domain / url hits",
66-
"query": "event.dns.request contains:anycase (\"shellhub.io\", \"sharedwithexpose.com\", \"sharedwithexpose.com\", \"eu-1.sharedwithexpose.com\", \"us-1.sharedwithexpose.com\", \"us-2.sharedwithexpose.com\", \"ap-1.sharedwithexpose.com\", \"in-1.sharedwithexpose.com\", \"sa-1.sharedwithexpose.com\", \"au-1.sharedwithexpose.com\", \"eu-2.sharedwithexpose.com\", \"tuns.sh\", \"ssi.sh\", \"tunnelto.dev\", \"pagekite.net\", \"pagekite.me\", \"loca.lt\", \"localtunnel.me\", \"dataplicity.com\", \"devtunnels.ms\", \"tunnels.api.visualstudio.com\", \"get.telebit.io\", \"telebit.io\", \"telebit.cloud\", \"telebit.fun\", \"devtunnels.ms\", \"data.rel.tunnels.api.visualstudio.com\", \"rel.tunnels.api.visualstudio.com\", \"global.rel.tunnels.api.visualstudio.com\", \"localhost.run\", \"pinggy.io\", \"pinggy.link\", \"amazonaws.com/public.pinggy.binaries/\", \"loophole.eu.auth0.com\", \"loophole.site\", \"burrow.io\", \"burrow.link\", \"io.burrow.link:\", \"localto.net\", \"localtonet.com\", \"tmate.io\", \"tunnel.staqlab.com\", \"staqlab-tunnel.com\", \"staqlab.com\", \"devtunnels.ms\", \"tunnels.api.visualstudio.com\", \"jprq.io\", \"bore.pub:\", \"btunnel.co.in\", \"btunnel.in\", \"ngrok.com\", \"ngrok.io\", \"ngrok.dev\", \"ngrok.app\", \"ngrok.pro\", \"ngrok.pizza\", \"ngrok-agent.com\", \"ngrok-free.app\", \"ngrok-cname.com\", \"serveo.net\", \"pitunnel.com\", \"openport.io\", \"beeceptor.com\", \"loca.lt\", \"localtunnel.me\", \"localxpose.io\", \"expose.sh\", \"expos.es\", \"trycloudflare.com\", \"cfargotunnel.com\", \"cftunnel.com\", \"bore.digital:\", \"bore.digital\") or url.address contains:anycase (\"shellhub.io\", \"sharedwithexpose.com\", \"sharedwithexpose.com\", \"eu-1.sharedwithexpose.com\", \"us-1.sharedwithexpose.com\", \"us-2.sharedwithexpose.com\", \"ap-1.sharedwithexpose.com\", \"in-1.sharedwithexpose.com\", \"sa-1.sharedwithexpose.com\", \"au-1.sharedwithexpose.com\", \"eu-2.sharedwithexpose.com\", \"tuns.sh\", \"ssi.sh\", \"tunnelto.dev\", \"pagekite.net\", \"pagekite.me\", \"loca.lt\", \"localtunnel.me\", \"dataplicity.com\", \"devtunnels.ms\", \"tunnels.api.visualstudio.com\", \"get.telebit.io\", \"telebit.io\", \"telebit.cloud\", \"telebit.fun\", \"devtunnels.ms\", \"data.rel.tunnels.api.visualstudio.com\", \"rel.tunnels.api.visualstudio.com\", \"global.rel.tunnels.api.visualstudio.com\", \"localhost.run\", \"pinggy.io\", \"pinggy.link\", \"amazonaws.com/public.pinggy.binaries/\", \"loophole.eu.auth0.com\", \"loophole.site\", \"burrow.io\", \"burrow.link\", \"io.burrow.link:\", \"localto.net\", \"localtonet.com\", \"tmate.io\", \"tunnel.staqlab.com\", \"staqlab-tunnel.com\", \"staqlab.com\", \"devtunnels.ms\", \"tunnels.api.visualstudio.com\", \"jprq.io\", \"bore.pub:\", \"btunnel.co.in\", \"btunnel.in\", \"ngrok.com\", \"ngrok.io\", \"ngrok.dev\", \"ngrok.app\", \"ngrok.pro\", \"ngrok.pizza\", \"ngrok-agent.com\", \"ngrok-free.app\", \"ngrok-cname.com\", \"serveo.net\", \"pitunnel.com\", \"openport.io\", \"beeceptor.com\", \"loca.lt\", \"localtunnel.me\", \"localxpose.io\", \"expose.sh\", \"expos.es\", \"trycloudflare.com\", \"cfargotunnel.com\", \"cftunnel.com\", \"bore.digital:\", \"bore.digital\")\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, event.dns.request\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, event.dns.request, Count\n| sort -Count\n| limit 100000\n"
66+
"query": "event.dns.request contains:anycase (\"shellhub.io\", \"sharedwithexpose.com\", \"eu-1.sharedwithexpose.com\", \"us-1.sharedwithexpose.com\", \"us-2.sharedwithexpose.com\", \"ap-1.sharedwithexpose.com\", \"in-1.sharedwithexpose.com\", \"sa-1.sharedwithexpose.com\", \"au-1.sharedwithexpose.com\", \"eu-2.sharedwithexpose.com\", \"tuns.sh\", \"ssi.sh\", \"tunnelto.dev\", \"pagekite.net\", \"pagekite.me\", \"loca.lt\", \"localtunnel.me\", \"dataplicity.com\", \"devtunnels.ms\", \"tunnels.api.visualstudio.com\", \"get.telebit.io\", \"telebit.io\", \"telebit.cloud\", \"telebit.fun\", \"data.rel.tunnels.api.visualstudio.com\", \"rel.tunnels.api.visualstudio.com\", \"global.rel.tunnels.api.visualstudio.com\", \"localhost.run\", \"pinggy.io\", \"pinggy.link\", \"amazonaws.com/public.pinggy.binaries/\", \"loophole.eu.auth0.com\", \"loophole.site\", \"burrow.io\", \"burrow.link\", \"io.burrow.link\", \"localto.net\", \"localtonet.com\", \"tmate.io\", \"tunnel.staqlab.com\", \"staqlab-tunnel.com\", \"staqlab.com\", \"jprq.io\", \"bore.pub\", \"btunnel.co.in\", \"btunnel.in\", \"ngrok.com\", \"ngrok.io\", \"ngrok.dev\", \"ngrok.app\", \"ngrok.pro\", \"ngrok.pizza\", \"ngrok-agent.com\", \"ngrok-free.app\", \"ngrok-cname.com\", \"serveo.net\", \"pitunnel.com\", \"openport.io\", \"beeceptor.com\", \"localxpose.io\", \"expose.sh\", \"expos.es\", \"trycloudflare.com\", \"cfargotunnel.com\", \"cftunnel.com\", \"bore.digital\") or url.address contains:anycase (\"shellhub.io\", \"sharedwithexpose.com\", \"eu-1.sharedwithexpose.com\", \"us-1.sharedwithexpose.com\", \"us-2.sharedwithexpose.com\", \"ap-1.sharedwithexpose.com\", \"in-1.sharedwithexpose.com\", \"sa-1.sharedwithexpose.com\", \"au-1.sharedwithexpose.com\", \"eu-2.sharedwithexpose.com\", \"tuns.sh\", \"ssi.sh\", \"tunnelto.dev\", \"pagekite.net\", \"pagekite.me\", \"loca.lt\", \"localtunnel.me\", \"dataplicity.com\", \"devtunnels.ms\", \"tunnels.api.visualstudio.com\", \"get.telebit.io\", \"telebit.io\", \"telebit.cloud\", \"telebit.fun\", \"data.rel.tunnels.api.visualstudio.com\", \"rel.tunnels.api.visualstudio.com\", \"global.rel.tunnels.api.visualstudio.com\", \"localhost.run\", \"pinggy.io\", \"pinggy.link\", \"amazonaws.com/public.pinggy.binaries/\", \"loophole.eu.auth0.com\", \"loophole.site\", \"burrow.io\", \"burrow.link\", \"io.burrow.link\", \"localto.net\", \"localtonet.com\", \"tmate.io\", \"tunnel.staqlab.com\", \"staqlab-tunnel.com\", \"staqlab.com\", \"jprq.io\", \"bore.pub\", \"btunnel.co.in\", \"btunnel.in\", \"ngrok.com\", \"ngrok.io\", \"ngrok.dev\", \"ngrok.app\", \"ngrok.pro\", \"ngrok.pizza\", \"ngrok-agent.com\", \"ngrok-free.app\", \"ngrok-cname.com\", \"serveo.net\", \"pitunnel.com\", \"openport.io\", \"beeceptor.com\", \"localxpose.io\", \"expose.sh\", \"expos.es\", \"trycloudflare.com\", \"cfargotunnel.com\", \"cftunnel.com\", \"bore.digital\")\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, event.dns.request\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, event.dns.request, Count\n| sort -Count\n| limit 100000\n"
6767
},
6868
{
6969
"category": "Execution & Persistence",
@@ -148,7 +148,7 @@
148148
{
149149
"category": "Execution & TTPs",
150150
"name": "NetExec RDP Behaviour detected",
151-
"query": "((src.process.name contains:anycase ('| clip & exit' ,'clip; exit')) OR (tgt.process.cmdline contains:anycase ('| clip & exit' ,'clip; exit'))) OR (src.process.name contains:anycase (\"clip.exe\") AND src.parent.process.name contains:anycase (\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\",\"msiexec.exe\",\"cmd.exe\"))\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, Count\n| sort -Count\n| limit 100000\n"
151+
"query": "((src.process.name contains:anycase ('| clip & exit' ,'clip; exit')) OR (tgt.process.cmdline contains:anycase ('| clip & exit' ,'clip; exit'))) OR (src.process.name contains:anycase (\"clip.exe\") AND src.process.parent.name contains:anycase (\"powershell.exe\",\"pwsh.exe\",\"mshta.exe\",\"msiexec.exe\",\"cmd.exe\"))\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.user, src.process.parent.name, src.process.name, src.process.cmdline, Count\n| sort -Count\n| limit 100000\n"
152152
},
153153
{
154154
"category": "Credential Access",
@@ -258,7 +258,7 @@
258258
{
259259
"category": "Discovery & Reconnaissance",
260260
"name": "Burp Suite Tool Detected",
261-
"query": "(event.dns.request contains:anycase 'burpcollaborator.net' OR url.address contains:anycase 'burpcollaborator.net' AND (src.ip.address matches '^(10\\\\.|172\\\\.(1[6-9]|2[0-9]|3[0-1])\\\\.|192\\\\.168\\\\.|127\\\\.).*' OR dst.ip.address matches '^(10\\\\.|172\\\\.(1[6-9]|2[0-9]|3[0-1])\\\\.|192\\\\.168\\\\.|127\\\\.).*'))\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.name, event.dns.request, url.address, src.ip.address, dst.ip.address\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.name, event.dns.request, url.address, src.ip.address, dst.ip.address, Count\n| sort -Count\n| limit 100000"
261+
"query": "(event.dns.request contains:anycase 'burpcollaborator.net' OR url.address contains:anycase 'burpcollaborator.net') AND (src.ip.address matches '^(10\\\\.|172\\\\.(1[6-9]|2[0-9]|3[0-1])\\\\.|192\\\\.168\\\\.|127\\\\.).*' OR dst.ip.address matches '^(10\\\\.|172\\\\.(1[6-9]|2[0-9]|3[0-1])\\\\.|192\\\\.168\\\\.|127\\\\.).*'))\n| group _FirstSeenMs=min(event.time), _LastSeenMs=max(event.time), Count=count() by endpoint.name, src.process.name, event.dns.request, url.address, src.ip.address, dst.ip.address\n| let _firstSeenNs = _FirstSeenMs * 1000000\n| let _lastSeenNs = _LastSeenMs * 1000000\n| columns \"first.timestamp\" = _firstSeenNs, \"last.timestamp\" = _lastSeenNs, endpoint.name, src.process.name, event.dns.request, url.address, src.ip.address, dst.ip.address, Count\n| sort -Count\n| limit 100000"
262262
},
263263
{
264264
"category": "Lateral Movement",

userscript.js

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
const PINNED_QUERIES_KEY = "s1_pinned_hunting_queries";
1919

2020
let allFetchedQueries = [];
21+
let documentListenerController = null;
22+
let fetchInProgress = false;
2123

2224
const listIconSVG = `
2325
<img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%2077.09%2095.88%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%3E%0A%20%3Cdefs%3E%0A%20%20%3Cstyle%3E%0A%20%20%20.cls-1%7Bfill%3A%236b0aea%3Bfill-rule%3Aevenodd%3B%7D%0A%20%20%3C/style%3E%0A%20%3C/defs%3E%0A%20%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%0A%20%20%3Cg%20id%3D%22ART%22%3E%0A%20%20%20%3Cpath%20class%3D%22cls-1%22%20d%3D%22M32.08%2C0H45V77.25H32.08ZM48.13%2C95.88l12.91-8V21a32.21%2C32.21%2C0%2C0%2C0-12.91-5.72ZM16%2C87.92l12.92%2C8V15.32A32.19%2C32.19%2C0%2C0%2C0%2C16%2C21ZM64.17%2C3.67V86.48l6-3.72a15.3%2C15.3%2C0%2C0%2C0%2C6.89-13V30.65C77.09%2C19.37%2C64.17%2C3.67%2C64.17%2C3.67ZM0%2C69.73a15.27%2C15.27%2C0%2C0%2C0%2C6.89%2C13l6%2C3.72V3.67S0%2C19.37%2C0%2C30.65Z%22/%3E%0A%20%20%3C/g%3E%0A%20%3C/g%3E%0A%3C/svg%3E" style="height:1rem;" alt="Logo" srcset="">
@@ -58,20 +60,19 @@
5860
const queryTextarea = document.querySelector(
5961
'[data-test-id="power-query-input"]'
6062
);
63+
if (!queryTextarea) return;
6164
const searchButton = document.querySelector(
6265
'[data-test-id="power-query-search-button"]'
6366
);
64-
if (queryTextarea) {
65-
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
66-
window.HTMLTextAreaElement.prototype,
67-
"value"
68-
).set;
69-
nativeInputValueSetter.call(queryTextarea, query);
70-
["input", "change"].forEach((eventType) =>
71-
queryTextarea.dispatchEvent(new Event(eventType, { bubbles: true }))
72-
);
73-
queryTextarea.focus();
74-
}
67+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
68+
window.HTMLTextAreaElement.prototype,
69+
"value"
70+
).set;
71+
nativeInputValueSetter.call(queryTextarea, query);
72+
["input", "change"].forEach((eventType) =>
73+
queryTextarea.dispatchEvent(new Event(eventType, { bubbles: true }))
74+
);
75+
queryTextarea.focus();
7576
if (searchButton) searchButton.click();
7677
showNotification("Query executed successfully!");
7778
}
@@ -481,7 +482,7 @@
481482
const pinButton = document.createElement("button");
482483
pinButton.className = "hunting-queries-pin-btn";
483484
pinButton.innerHTML = starIconSVG;
484-
if (isQueryPinned(queryObj.name)) {
485+
if (pinnedQueryNames.includes(queryObj.name)) {
485486
pinButton.classList.add("pinned");
486487
pinButton.title = "Unpin query";
487488
} else {
@@ -576,10 +577,12 @@
576577
tableButtonToolbarItem.nextSibling
577578
);
578579

580+
let searchDebounceTimer;
579581
searchInput.addEventListener("input", (e) => {
580582
const value = e.target.value;
581583
clearButton.style.display = value ? "block" : "none";
582-
renderQueryItems(value);
584+
clearTimeout(searchDebounceTimer);
585+
searchDebounceTimer = setTimeout(() => renderQueryItems(value), 150);
583586
});
584587
searchInput.addEventListener("keydown", (e) => {
585588
if (e.key === "Escape") closeDropdown();
@@ -603,20 +606,24 @@
603606
renderQueryItems(searchInput.value);
604607
}
605608
});
609+
if (documentListenerController) documentListenerController.abort();
610+
documentListenerController = new AbortController();
611+
const { signal: docSignal } = documentListenerController;
612+
606613
document.addEventListener("click", (e) => {
607614
if (
608615
!dropdownDiv.contains(e.target) &&
609616
dropdownMenu.classList.contains("show")
610617
)
611618
closeDropdown();
612-
});
619+
}, { signal: docSignal });
613620

614621
document.addEventListener("pinnedQueryChange", () => {
615622
if (dropdownMenu.classList.contains("show")) {
616623
renderTabs();
617624
renderQueryItems(searchInput.value);
618625
}
619-
});
626+
}, { signal: docSignal });
620627

621628
renderTabs();
622629
renderQueryItems();
@@ -865,6 +872,7 @@
865872
method: "GET",
866873
url: QUERIES_URL,
867874
onload: function (response) {
875+
fetchInProgress = false;
868876
try {
869877
const queries = JSON.parse(response.responseText);
870878
allFetchedQueries = queries;
@@ -878,6 +886,7 @@
878886
}
879887
},
880888
onerror: function (error) {
889+
fetchInProgress = false;
881890
console.error("Error fetching queries:", error);
882891
allFetchedQueries = null;
883892
addCustomQueryButton(null);
@@ -898,7 +907,8 @@
898907
const tableButton = powerQueryPage.querySelector(
899908
'[data-test-id="graph-style-dropdown"]'
900909
);
901-
if (toolbar && tableButton) {
910+
if (toolbar && tableButton && !fetchInProgress) {
911+
fetchInProgress = true;
902912
fetchQueriesAndInject();
903913
}
904914
}

0 commit comments

Comments
 (0)