diff --git a/frontend/components/ActionsDropdown/ActionsDropdown.tsx b/frontend/components/ActionsDropdown/ActionsDropdown.tsx
index bbbe12cb83e..2b9cbad09e9 100644
--- a/frontend/components/ActionsDropdown/ActionsDropdown.tsx
+++ b/frontend/components/ActionsDropdown/ActionsDropdown.tsx
@@ -31,7 +31,7 @@ interface IActionsDropdownProps {
variant?: "button" | "brand-button" | "small-button";
}
-const getOptionBackgroundColor = (state: any) => {
+const getOptionBackgroundColor = (state: { isFocused: boolean }) => {
return state.isFocused ? COLORS["ui-fleet-black-5"] : "transparent";
};
diff --git a/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx b/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx
index 598960d6b31..e4d2d5887ff 100644
--- a/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx
+++ b/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx
@@ -7,7 +7,6 @@ import {
baseUrl,
} from "test/test-utils";
import mockServer from "test/mock-server";
-import { getDeviceVppCommandResultHandler } from "test/handlers/device-handler";
import {
createMockHostAppStoreApp,
createMockHostSoftware,
diff --git a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx
index acecc019820..98e87ecfa7a 100644
--- a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx
+++ b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx
@@ -60,9 +60,9 @@ const platformSubNav: IPlatformSubNav[] = [
interface IPlatformWrapperProps {
enrollSecret: string;
onCancel: () => void;
- certificate: any;
+ certificate: string | undefined;
isFetchingCertificate: boolean;
- fetchCertificateError: any;
+ fetchCertificateError: Error | null;
config: IConfig | null;
}
@@ -419,7 +419,7 @@ const PlatformWrapper = ({
{fetchCertificateError ? (
- {fetchCertificateError}
+ {fetchCertificateError?.message}
) : (
diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTable.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTable.tsx
index 7d6e7cd9f08..9d30d19d887 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTable.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTable.tsx
@@ -253,7 +253,7 @@ const QueriesTable = ({
variant="table-filter"
/>
);
- }, [curTargetedPlatformFilter, queryParams, router]);
+ }, [curTargetedPlatformFilter, handlePlatformFilterDropdownChange]);
const columnConfigs = useMemo(
() =>
diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
index 60b0d4d76ef..35ef2eb2f64 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx
@@ -290,8 +290,7 @@ const generateColumnConfigs = ({
if (canEditQueries && !omitSelectionColumn) {
tableHeaders.unshift({
id: "selection",
- // TODO - improve typing of IHeaderProps instead of using any
- // Header: (headerProps: IHeaderProps): JSX.Element => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
Header: (headerProps: any): JSX.Element => {
const checkboxProps = getConditionalSelectHeaderCheckboxProps({
headerProps,
diff --git a/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx b/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx
index a50cfb50a06..bd19af4a47a 100644
--- a/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx
+++ b/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx
@@ -214,7 +214,7 @@ const QueryDetailsPage = ({
);
setCurrentTeam(querysTeam);
}
- }, [storedQuery]);
+ }, [storedQuery, availableTeams, setCurrentTeam]);
// Updates title that shows up on browser tabs
useEffect(() => {
diff --git a/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx b/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx
index 8ded5041eae..298fadfcb2d 100644
--- a/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx
+++ b/frontend/pages/queries/details/components/QueryReport/QueryReport.tsx
@@ -65,6 +65,7 @@ const QueryReport = ({
setColumnConfigs(newColumnConfigs);
}
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryReport]); // Cannot use tableHeaders as it will cause infinite loop with setTableHeaders
const onExportQueryResults = (evt: React.MouseEvent
) => {
diff --git a/frontend/pages/queries/edit/EditQueryPage.tsx b/frontend/pages/queries/edit/EditQueryPage.tsx
index 71c930b97c2..974d34d8b0f 100644
--- a/frontend/pages/queries/edit/EditQueryPage.tsx
+++ b/frontend/pages/queries/edit/EditQueryPage.tsx
@@ -189,7 +189,7 @@ const EditQueryPage = ({
);
setCurrentTeam(querysTeam);
}
- }, [storedQuery]);
+ }, [storedQuery, availableTeams, setCurrentTeam]);
const detectIsFleetQueryRunnable = () => {
statusAPI.live_query().catch(() => {
@@ -220,7 +220,17 @@ const EditQueryPage = ({
})
);
}
- }, [queryId, isTeamMaintainerOrTeamAdmin, isStoredQueryLoading]);
+ }, [
+ queryId,
+ isTeamMaintainerOrTeamAdmin,
+ isStoredQueryLoading,
+ isGlobalAdmin,
+ isGlobalMaintainer,
+ location.query.fleet_id,
+ location.query.host_id,
+ router,
+ storedQuery?.team_id,
+ ]);
useEffect(() => {
detectIsFleetQueryRunnable();
@@ -237,7 +247,19 @@ const EditQueryPage = ({
setLastEditedQueryPlatforms(DEFAULT_QUERY.platform);
setLastEditedQueryDiscardData(DEFAULT_QUERY.discard_data);
}
- }, [queryId]);
+ }, [
+ queryId,
+ setLastEditedQueryId,
+ setLastEditedQueryName,
+ setLastEditedQueryDescription,
+ setLastEditedQueryObserverCanRun,
+ setLastEditedQueryFrequency,
+ setLastEditedQueryAutomationsEnabled,
+ setLastEditedQueryLoggingType,
+ setLastEditedQueryMinOsqueryVersion,
+ setLastEditedQueryPlatforms,
+ setLastEditedQueryDiscardData,
+ ]);
const [isQuerySaving, setIsQuerySaving] = useState(false);
const [isQueryUpdating, setIsQueryUpdating] = useState(false);
@@ -272,7 +294,7 @@ const EditQueryPage = ({
);
renderFlash("success", "Report created.");
setBackendValidators({});
- } catch (createError: any) {
+ } catch (createError: unknown) {
if (getErrorReason(createError).includes("already exists")) {
const teamErrorText =
teamNameForQuery && apiTeamIdForQuery !== 0
@@ -318,7 +340,7 @@ const EditQueryPage = ({
await queryAPI.update(queryId, updatedQuery);
renderFlash("success", "Report updated.");
refetchStoredQuery(); // Required to compare recently saved query to a subsequent save to the query
- } catch (updateError: any) {
+ } catch (updateError: unknown) {
console.error(updateError);
const reason = getErrorReason(updateError);
if (reason.includes("Duplicate")) {
diff --git a/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx b/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx
index fc651298484..8de787e6923 100644
--- a/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx
+++ b/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tests.tsx
@@ -154,7 +154,7 @@ describe("EditQueryForm - component", () => {
},
});
- const { container, user } = render(
+ const { user } = render(
({}); // string | null | undefined or boolean | undefined
+ const [errors, setErrors] = useState>({});
// handles saving a copy of an existing query as a new query
const [showSaveAsNewQueryModal, setShowSaveAsNewQueryModal] = useState(false);
@@ -253,7 +246,7 @@ const EditQueryForm = ({
};
}, {}) || {}
);
- }, [storedQuery]);
+ }, [storedQuery, isPremiumTier]);
const {
data: { labels } = { labels: [] },
@@ -288,7 +281,14 @@ const EditQueryForm = ({
setCompatiblePlatforms(lastEditedQueryBody);
}
debounceSQL(lastEditedQueryBody);
- }, [lastEditedQueryBody, lastEditedQueryId, isStoredQueryLoading]);
+ }, [
+ lastEditedQueryBody,
+ lastEditedQueryId,
+ isStoredQueryLoading,
+ debounceSQL,
+ queryIdForEdit,
+ setCompatiblePlatforms,
+ ]);
const toggleSaveNewQueryModal = () => {
setShowSaveNewQueryModal(!showSaveNewQueryModal);
@@ -333,9 +333,7 @@ const EditQueryForm = ({
// it's safe to assume that frequency is a number
(frequency) => `Every ${secondsToDhms(frequency as number)}`
),
- // intentionally leave lastEditedQueryFrequency out of the dependencies, so that the custom
- // options are maintained even if the user changes the frequency in the UI
- []
+ [lastEditedQueryFrequency]
);
const onSelectLabel = ({
@@ -380,10 +378,11 @@ const EditQueryForm = ({
evt.preventDefault();
if (isExistingQuery && !lastEditedQueryName) {
- return setErrors({
+ setErrors({
...errors,
name: "Report name must be present",
});
+ return;
}
const { valid, errors: newErrs } = validateQuerySQL(lastEditedQueryBody);
diff --git a/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx b/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx
index 87cfdd06caf..9605534d31d 100644
--- a/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx
+++ b/frontend/pages/queries/edit/components/QueryResults/QueryResults.tsx
@@ -105,7 +105,7 @@ const QueryResults = ({
setResultsColumnConfigs(newResultsColumnConfigs);
}
}
- }, [queryResults, lastEditedQueryBody]); // Cannot use tableHeaders as it will cause infinite loop with setTableHeaders
+ }, [queryResults, lastEditedQueryBody, resultsColumnConfigs]);
useEffect(() => {
if (errorColumnConfigs?.length === 0 && !!errors?.length) {
@@ -120,7 +120,7 @@ const QueryResults = ({
}
}
}
- }, [errors]); // Cannot use errorTableHeaders as it will cause infinite loop with setErrorTableHeaders
+ }, [errors, errorColumnConfigs, resultsColumnConfigs]);
const onExportQueryResults = (evt: React.MouseEvent) => {
evt.preventDefault();
diff --git a/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx b/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx
index db4cb8dff18..45ca86e4e77 100644
--- a/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx
+++ b/frontend/pages/queries/live/LiveQueryPage/LiveQueryPage.tsx
@@ -148,7 +148,12 @@ const RunQueryPage = ({
labels: targetedLabels,
teams: targetedTeams,
});
- }, [targetedLabels, targetedHosts, targetedTeams]);
+ }, [
+ targetedLabels,
+ targetedHosts,
+ targetedTeams,
+ setSelectedQueryTargetsByType,
+ ]);
// Updates title that shows up on browser tabs
useEffect(() => {
@@ -164,7 +169,7 @@ const RunQueryPage = ({
const path = queryId ? PATHS.EDIT_REPORT(queryId) : PATHS.NEW_REPORT;
router.push(getPathWithQueryParams(path, { fleet_id: currentTeamId }));
- }, []);
+ }, [currentTeamId, queryId, router]);
const renderScreen = () => {
const step1Props = {
diff --git a/frontend/pages/queries/live/screens/RunQuery.tsx b/frontend/pages/queries/live/screens/RunQuery.tsx
index 4697422058c..cccc5489d89 100644
--- a/frontend/pages/queries/live/screens/RunQuery.tsx
+++ b/frontend/pages/queries/live/screens/RunQuery.tsx
@@ -1,4 +1,11 @@
-import React, { useState, useEffect, useRef, useContext } from "react";
+import React, {
+ useState,
+ useEffect,
+ useRef,
+ useContext,
+ useCallback,
+ useMemo,
+} from "react";
import SockJS from "sockjs-client";
import { QueryContext } from "context/query";
@@ -50,21 +57,21 @@ const RunQuery = ({
const isStoredQueryEdited = storedQuery?.query !== lastEditedQueryBody;
const ws = useRef(null);
- const runQueryInterval = useRef(null);
- const globalSocket = useRef(null);
- const previousSocketData = useRef(null);
+ const runQueryInterval = useRef | null>(null);
+ const globalSocket = useRef(null);
+ const previousSocketData = useRef(null);
const responseCount = useRef({ ...RESPONSE_COUNT_ZERO });
- const removeSocket = () => {
+ const removeSocket = useCallback(() => {
if (globalSocket.current) {
globalSocket.current.close();
globalSocket.current = null;
previousSocketData.current = null;
responseCount.current = RESPONSE_COUNT_ZERO;
}
- };
+ }, []);
- const setupDistributedQuery = (socket: WebSocket | null) => {
+ const setupDistributedQuery = useCallback((socket: WebSocket | null) => {
globalSocket.current = socket;
const update = () => {
setCampaignState((prevCampaignState) => ({
@@ -76,9 +83,9 @@ const RunQuery = ({
if (!runQueryInterval.current) {
runQueryInterval.current = setInterval(update, 1000);
}
- };
+ }, []);
- const teardownDistributedQuery = () => {
+ const teardownDistributedQuery = useCallback(() => {
if (runQueryInterval.current) {
clearInterval(runQueryInterval.current);
runQueryInterval.current = null;
@@ -91,128 +98,151 @@ const RunQuery = ({
}));
setIsQueryFinished(true);
removeSocket();
- };
+ }, [removeSocket]);
- const destroyCampaign = () => {
+ const destroyCampaign = useCallback(() => {
setCampaignState(DEFAULT_CAMPAIGN_STATE);
- };
-
- const connectAndRunLiveQuery = (returnedCampaign: ICampaign) => {
- let { current: websocket }: { current: WebSocket | null } = ws;
- websocket = new SockJS(`${BASE_URL}/v1/fleet/results`, undefined, {});
- websocket.onopen = () => {
- setupDistributedQuery(websocket);
- // `prevCampaignState` at this point is the default state. Update that with what we get from
- // the API response
- setCampaignState((prevCampaignState) => ({
- ...prevCampaignState,
- campaign: { ...prevCampaignState.campaign, returnedCampaign },
- queryIsRunning: true,
- }));
-
- websocket?.send(
- JSON.stringify({
- type: "auth",
- data: { token: authToken.get() },
- })
- );
- websocket?.send(
- JSON.stringify({
- type: "select_campaign",
- data: { campaign_id: returnedCampaign.id },
- })
- );
- };
-
- websocket.onmessage = ({ data }: { data: string }) => {
- // string is easy to compare before converting to object
- if (data === previousSocketData.current) {
- return false;
- }
+ }, []);
- previousSocketData.current = data;
- const socketData = JSON.parse(data);
- setCampaignState((prevCampaignState) => {
- return {
+ const connectAndRunLiveQuery = useCallback(
+ (returnedCampaign: ICampaign) => {
+ let { current: websocket }: { current: WebSocket | null } = ws;
+ websocket = new SockJS(`${BASE_URL}/v1/fleet/results`, undefined, {});
+ websocket.onopen = () => {
+ setupDistributedQuery(websocket);
+ // `prevCampaignState` at this point is the default state. Update that with what we get from
+ // the API response
+ setCampaignState((prevCampaignState) => ({
...prevCampaignState,
- ...campaignHelpers.updateCampaignState(socketData)(prevCampaignState),
- };
- });
- responseCount.current.results += socketData?.data?.rows?.length ?? 0;
- responseCount.current.errors += socketData?.data?.error ? 1 : 0;
-
- if (
- socketData.type === "status" &&
- socketData.data.status === "finished"
- ) {
- return teardownDistributedQuery();
- }
- if (
- responseCount.current.results + responseCount.current.errors >=
- CAMPAIGN_LIMIT
- ) {
- teardownDistributedQuery();
- setIsQueryClipped(true);
- }
- };
- };
-
- const onRunQuery = debounce(async () => {
- if (!lastEditedQueryBody) {
- renderFlash(
- "error",
- "Something went wrong running your report. Please try again."
- );
- return false;
- }
-
- const selected = formatSelectedTargetsForApi(selectedTargets);
- setIsQueryFinished(false);
- removeSocket();
- destroyCampaign();
-
- try {
- const returnedCampaign = await queryAPI.run({
- query: lastEditedQueryBody,
- queryId: isStoredQueryEdited ? null : queryId, // we treat edited SQL as a new query
- selected,
- });
-
- connectAndRunLiveQuery(returnedCampaign);
- } catch (campaignError: any) {
- const err = campaignError.toString();
- if (err.includes("no hosts targeted")) {
- renderFlash(
- "error",
- "Your target selections did not include any hosts. Please try again."
- );
- } else if (err.includes("resource already created")) {
- renderFlash(
- "error",
- "A campaign with the provided query text has already been created"
+ campaign: { ...prevCampaignState.campaign, returnedCampaign },
+ queryIsRunning: true,
+ }));
+
+ websocket?.send(
+ JSON.stringify({
+ type: "auth",
+ data: { token: authToken.get() },
+ })
);
- } else if (err.includes("forbidden") || err.includes("unauthorized")) {
- renderFlash(
- "error",
- "It seems you do not have the rights to run this report. If you believe this is an error, please contact your administrator."
+ websocket?.send(
+ JSON.stringify({
+ type: "select_campaign",
+ data: { campaign_id: returnedCampaign.id },
+ })
);
- } else {
- renderFlash("error", "Something has gone wrong. Please try again.");
- }
+ };
+
+ websocket.onmessage = ({ data }: { data: string }) => {
+ // string is easy to compare before converting to object
+ if (data === previousSocketData.current) {
+ return;
+ }
+
+ previousSocketData.current = data;
+ const socketData = JSON.parse(data);
+ setCampaignState((prevCampaignState) => {
+ return {
+ ...prevCampaignState,
+ ...campaignHelpers.updateCampaignState(socketData)(
+ prevCampaignState
+ ),
+ };
+ });
+ responseCount.current.results += socketData?.data?.rows?.length ?? 0;
+ responseCount.current.errors += socketData?.data?.error ? 1 : 0;
+
+ if (
+ socketData.type === "status" &&
+ socketData.data.status === "finished"
+ ) {
+ teardownDistributedQuery();
+ return;
+ }
+ if (
+ responseCount.current.results + responseCount.current.errors >=
+ CAMPAIGN_LIMIT
+ ) {
+ teardownDistributedQuery();
+ setIsQueryClipped(true);
+ }
+ };
+ },
+ [setupDistributedQuery, teardownDistributedQuery]
+ );
- return teardownDistributedQuery();
- }
- });
+ const onRunQuery = useMemo(
+ () =>
+ debounce(async () => {
+ if (!lastEditedQueryBody) {
+ renderFlash(
+ "error",
+ "Something went wrong running your report. Please try again."
+ );
+ return;
+ }
+
+ const selected = formatSelectedTargetsForApi(selectedTargets);
+ setIsQueryFinished(false);
+ removeSocket();
+ destroyCampaign();
+
+ try {
+ const returnedCampaign = await queryAPI.run({
+ query: lastEditedQueryBody,
+ queryId: isStoredQueryEdited ? null : queryId, // we treat edited SQL as a new query
+ selected,
+ });
+
+ connectAndRunLiveQuery(returnedCampaign);
+ } catch (campaignError: unknown) {
+ const err = String(campaignError);
+ if (err.includes("no hosts targeted")) {
+ renderFlash(
+ "error",
+ "Your target selections did not include any hosts. Please try again."
+ );
+ } else if (err.includes("resource already created")) {
+ renderFlash(
+ "error",
+ "A campaign with the provided query text has already been created"
+ );
+ } else if (
+ err.includes("forbidden") ||
+ err.includes("unauthorized")
+ ) {
+ renderFlash(
+ "error",
+ "It seems you do not have the rights to run this report. If you believe this is an error, please contact your administrator."
+ );
+ } else {
+ renderFlash("error", "Something has gone wrong. Please try again.");
+ }
+
+ teardownDistributedQuery();
+ }
+ }),
+ [
+ lastEditedQueryBody,
+ renderFlash,
+ selectedTargets,
+ isStoredQueryEdited,
+ queryId,
+ removeSocket,
+ destroyCampaign,
+ connectAndRunLiveQuery,
+ teardownDistributedQuery,
+ ]
+ );
const onStopQuery = (evt: React.MouseEvent) => {
evt.preventDefault();
- return teardownDistributedQuery();
+ teardownDistributedQuery();
};
useEffect(() => {
onRunQuery();
- }, []);
+ }, [onRunQuery]);
const { campaign } = campaignState;
return (
diff --git a/frontend/router/components/AuthenticatedRoutes/AuthenticatedRoutes.tsx b/frontend/router/components/AuthenticatedRoutes/AuthenticatedRoutes.tsx
index 5837bc91a0a..eedd651b078 100644
--- a/frontend/router/components/AuthenticatedRoutes/AuthenticatedRoutes.tsx
+++ b/frontend/router/components/AuthenticatedRoutes/AuthenticatedRoutes.tsx
@@ -11,6 +11,7 @@ import permissions from "utilities/permissions";
interface IAppProps {
children: JSX.Element;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
location: any; // no type in react-router v3
router: InjectedRouter;
}
@@ -83,20 +84,23 @@ export const AuthenticatedRoutes = ({
window.location.href = "https://www.fleetdm.com/try-fleet/login";
}
- return redirectToLogin();
+ redirectToLogin();
+ return;
}
if (currentUser?.force_password_reset && !authToken.get()) {
- return redirectToPasswordReset();
+ redirectToPasswordReset();
+ return;
}
if (currentUser?.api_only) {
- return redirectToApiUserOnly();
+ redirectToApiUserOnly();
+ return;
}
if (currentUser && permissions.isNoAccess(currentUser)) {
authToken.remove();
- return handlePageError({ status: 403 });
+ handlePageError({ status: 403 });
}
}, [currentUser]);
diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx
index d340535395f..0e7ee306806 100644
--- a/frontend/router/index.tsx
+++ b/frontend/router/index.tsx
@@ -114,6 +114,7 @@ const CustomQueryClientProvider: FC = QueryClie
interface IAppWrapperProps {
children: JSX.Element;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
location?: any;
}
diff --git a/frontend/services/entities/config.ts b/frontend/services/entities/config.ts
index 685826a7dcb..8b7bb0cf7e2 100644
--- a/frontend/services/entities/config.ts
+++ b/frontend/services/entities/config.ts
@@ -43,7 +43,7 @@ export default {
* responsible for verifying that the value of the rejected promise is an AxiosError
* and futher parsing of the the error mesage.
*/
- update: (formData: any, skipParseError?: boolean) => {
+ update: (formData: object, skipParseError?: boolean) => {
const { CONFIG } = endpoints;
return sendRequest(
diff --git a/frontend/services/entities/global_scheduled_queries.ts b/frontend/services/entities/global_scheduled_queries.ts
index a7eef64cc63..ee2dcac9cbe 100644
--- a/frontend/services/entities/global_scheduled_queries.ts
+++ b/frontend/services/entities/global_scheduled_queries.ts
@@ -2,11 +2,14 @@
import sendRequest from "services";
import endpoints from "utilities/endpoints";
-import { IEditScheduledQuery } from "interfaces/scheduled_query";
+import {
+ IEditScheduledQuery,
+ IPackQueryFormData,
+} from "interfaces/scheduled_query";
import helpers from "utilities/helpers";
export default {
- create: (formData: any) => {
+ create: (formData: IPackQueryFormData) => {
const { GLOBAL_SCHEDULE } = endpoints;
const {
@@ -47,7 +50,7 @@ export default {
},
update: (
globalScheduledQuery: IEditScheduledQuery,
- updatedAttributes: any
+ updatedAttributes: IPackQueryFormData
) => {
const { GLOBAL_SCHEDULE } = endpoints;
const path = `${GLOBAL_SCHEDULE}/${globalScheduledQuery.id}`;
diff --git a/frontend/services/entities/mdm_android.ts b/frontend/services/entities/mdm_android.ts
index 49d15062446..f795a1076af 100644
--- a/frontend/services/entities/mdm_android.ts
+++ b/frontend/services/entities/mdm_android.ts
@@ -46,6 +46,7 @@ export default {
const reader = response?.body?.getReader();
const decoder = new TextDecoder();
+ // eslint-disable-next-line no-constant-condition
while (true) {
// @ts-ignore
// eslint-disable-next-line no-await-in-loop
diff --git a/frontend/services/entities/osquery_options.ts b/frontend/services/entities/osquery_options.ts
index 821a2e90a38..284c6e71d51 100644
--- a/frontend/services/entities/osquery_options.ts
+++ b/frontend/services/entities/osquery_options.ts
@@ -10,10 +10,10 @@ export default {
return sendRequest("GET", OSQUERY_OPTIONS);
},
- update: (agentOptions: any, endpoint: string) => {
+ update: (agentOptions: unknown, endpoint: string) => {
return sendRequest("POST", endpoint, agentOptions);
},
- updateTeam: (teamId: number | undefined, agentOptions: any) => {
+ updateTeam: (teamId: number | undefined, agentOptions: unknown) => {
if (!teamId || teamId <= API_NO_TEAM_ID) {
return Promise.reject(
new Error(
diff --git a/frontend/services/entities/scripts.ts b/frontend/services/entities/scripts.ts
index fa65534cdb6..7fc7d16e5ac 100644
--- a/frontend/services/entities/scripts.ts
+++ b/frontend/services/entities/scripts.ts
@@ -6,11 +6,6 @@ import {
} from "interfaces/script";
import sendRequest from "services";
-import {
- createMockBatchScriptSummary,
- createMockScriptBatchHostResults,
-} from "__mocks__/scriptMock";
-
import endpoints from "utilities/endpoints";
import { buildQueryStringFromParams } from "utilities/url";
import {
@@ -107,7 +102,7 @@ export interface IScriptBatchSupportedFilters {
query?: string;
label_id?: number;
team_id?: number;
- status: any; // TODO - improve upstream typing
+ status: string | undefined; // TODO - improve upstream typing
}
interface IRunScriptBatchRequestBase {
script_id: number;
diff --git a/frontend/services/entities/software.ts b/frontend/services/entities/software.ts
index ebd60aa310e..a1e02e1b8f4 100644
--- a/frontend/services/entities/software.ts
+++ b/frontend/services/entities/software.ts
@@ -9,7 +9,7 @@ import {
encodeScriptBase64,
SCRIPTS_ENCODED_HEADER,
} from "utilities/scripts_encoding";
-import software, {
+import {
ISoftwareResponse,
ISoftwareCountResponse,
ISoftwareVersion,
diff --git a/frontend/services/entities/teams.ts b/frontend/services/entities/teams.ts
index 4ad78cfb9af..d4ed99c5442 100644
--- a/frontend/services/entities/teams.ts
+++ b/frontend/services/entities/teams.ts
@@ -163,7 +163,10 @@ export default {
/**
* updates the team config. This can take any partial data that is in the team config.
*/
- updateConfig: (data: any, teamId: number): Promise => {
+ updateConfig: (
+ data: Record,
+ teamId: number
+ ): Promise => {
const { TEAMS } = endpoints;
const path = `${TEAMS}/${teamId}`;
return sendRequest("PATCH", path, data);
diff --git a/frontend/services/entities/users.ts b/frontend/services/entities/users.ts
index 5f3a033648f..20dfa28f3d5 100644
--- a/frontend/services/entities/users.ts
+++ b/frontend/services/entities/users.ts
@@ -6,11 +6,13 @@ import { buildQueryStringFromParams } from "utilities/url";
import {
ICreateUserFormData,
+ IResetPasswordForm,
IUpdateUserFormData,
IUser,
ICreateUserWithInvitationFormData,
} from "interfaces/user";
import { ITeamSummary } from "interfaces/team";
+import type { IRegistrationFormData } from "interfaces/registration_form_data";
import { IUserSettings } from "interfaces/config";
export interface ISortOption {
@@ -143,12 +145,14 @@ export default {
helpers.addGravatarUrlToResource(response.user)
);
},
- resetPassword: (formData: any) => {
+ resetPassword: (
+ formData: IResetPasswordForm & { password_reset_token: string }
+ ) => {
const { RESET_PASSWORD } = endpoints;
return sendRequest("POST", RESET_PASSWORD, formData);
},
- setup: (formData: any) => {
+ setup: (formData: IRegistrationFormData) => {
const { SETUP } = endpoints;
const setupData = helpers.setupData(formData);
diff --git a/frontend/services/mock_service/mocks/config.ts b/frontend/services/mock_service/mocks/config.ts
index 08f93b68529..9ae9da60fa0 100644
--- a/frontend/services/mock_service/mocks/config.ts
+++ b/frontend/services/mock_service/mocks/config.ts
@@ -4,7 +4,7 @@ type MockResponse = Record;
type MockResponseFunction = (
url: string,
- data?: any
+ data?: Record
) => MockResponse | Promise;
export type MockEndpointHandler = MockResponse | MockResponseFunction;
diff --git a/frontend/services/mock_service/mocks/responses.ts b/frontend/services/mock_service/mocks/responses.ts
index 4610eef05a3..59a36102754 100644
--- a/frontend/services/mock_service/mocks/responses.ts
+++ b/frontend/services/mock_service/mocks/responses.ts
@@ -10633,19 +10633,20 @@ const secrets = (url: string) => {
};
};
-const addSecret = (url: string, secret: any) => {
+const addSecret = (url: string, secret: Record) => {
// Stubbed out for now, as the secrets endpoint is not yet implemented.
- if (secret.name === "DUPE") {
+ const name = secret.name as string;
+ if (name === "DUPE") {
return Promise.reject({ status: 409, message: "Conflict" });
}
- if (secret.name === "ERR") {
+ if (name === "ERR") {
return Promise.reject({ status: 500, message: "Internal Server Error" });
}
mockSecrets = [
...mockSecrets,
{
- name: secret.name,
+ name,
id: (nextSecretId += 1),
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
diff --git a/frontend/services/mock_service/service/service.ts b/frontend/services/mock_service/service/service.ts
index 2d01ec89f62..eb132e9e428 100644
--- a/frontend/services/mock_service/service/service.ts
+++ b/frontend/services/mock_service/service/service.ts
@@ -81,6 +81,7 @@ export const sendRequest = async (
method = "GET",
requestPath: string,
data?: unknown
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise => {
console.log("Mock service request URL: ", requestPath);
console.log("Mock service request body: ", data);
@@ -111,7 +112,7 @@ export const sendRequest = async (
const handler = methodHandlers?.[responseKey];
if (typeof handler === "function") {
- response = await handler(requestPath, data);
+ response = await handler(requestPath, data as Record);
} else {
response = handler;
}
diff --git a/frontend/test/test-utils.tsx b/frontend/test/test-utils.tsx
index 8fe8d13a0d2..4ab8af3ce55 100644
--- a/frontend/test/test-utils.tsx
+++ b/frontend/test/test-utils.tsx
@@ -78,7 +78,9 @@ interface IWrapperComponentProps {
}
const createWrapperComponent = (
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
CurrentWrapper: React.FC>, // TODO: types
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
WrapperComponent: React.FC>, // TODO: types
props: IWrapperComponentProps
) => {
diff --git a/frontend/typings/react-table-config.d.ts b/frontend/typings/react-table-config.d.ts
index cb7d717323b..9d8d0b5430c 100644
--- a/frontend/typings/react-table-config.d.ts
+++ b/frontend/typings/react-table-config.d.ts
@@ -65,6 +65,7 @@ declare module "react-table" {
// note that having Record here allows you to add anything to the options, this matches the spirit of the
// underlying js library, but might be cleaner if it's replaced by a more specific type that matches your
// feature set, this is a safe default.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
Record {}
export interface Hooks<
@@ -116,6 +117,7 @@ declare module "react-table" {
export interface Cell<
D extends Record = Record,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
V = any
> extends UseGroupByCellProps,
UseRowStateCellProps {}
diff --git a/frontend/utilities/append_target_type_to_targets/index.ts b/frontend/utilities/append_target_type_to_targets/index.ts
index 68c97e07dc4..638a3afc8ec 100644
--- a/frontend/utilities/append_target_type_to_targets/index.ts
+++ b/frontend/utilities/append_target_type_to_targets/index.ts
@@ -35,7 +35,7 @@ export const parseEntityFunc = (host: IHost) => {
};
};
-const appendTargetTypeToTargets = (targets: any, targetType: string) => {
+const appendTargetTypeToTargets = (targets: IHost[], targetType: string) => {
return map(targets, (target) => {
if (targetType === "hosts") {
return parseEntityFunc(target);
diff --git a/frontend/utilities/constants.tsx b/frontend/utilities/constants.tsx
index ec8d5fb0f61..76d65263ddb 100644
--- a/frontend/utilities/constants.tsx
+++ b/frontend/utilities/constants.tsx
@@ -469,7 +469,7 @@ export const HOST_OSQUERY_DATA = [
export const DEFAULT_USE_QUERY_OPTIONS = {
refetchOnWindowFocus: false,
retry: (failureCount: number, error: unknown) => {
- const err = error as any;
+ const err = error as { status?: number };
let isBadRequestErr = false;
if (err.status !== undefined) {
isBadRequestErr = err.status >= 400 && err.status < 500;
diff --git a/frontend/utilities/convert_to_csv/index.ts b/frontend/utilities/convert_to_csv/index.ts
index 97bec1506b8..00912b11d2a 100644
--- a/frontend/utilities/convert_to_csv/index.ts
+++ b/frontend/utilities/convert_to_csv/index.ts
@@ -1,30 +1,34 @@
const defaultFieldSortFunc = (fields: string[]) => fields;
interface ConvertToCSV {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
objArray: any[]; // TODO: typing
fieldSortFunc?: (fields: string[]) => string[];
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
tableHeaders?: any[]; // TODO: typing
}
-const formatFieldForCSV = (value: any): string => {
+const formatFieldForCSV = (value: unknown): string => {
+ let strValue: string;
+
// If the value is an object, stringify it first
if (typeof value === "object") {
- value = JSON.stringify(value);
+ strValue = JSON.stringify(value);
+ } else {
+ strValue = String(value);
}
// Treat values with leading zeros as strings so csv file doesn't trim leading zeros
- if (/^0\d+$/.test(value)) {
- return `"=""${value}"""`;
+ if (/^0\d+$/.test(strValue)) {
+ return `"=""${strValue}"""`;
}
// Escape double quotes in the value by doubling them
- if (typeof value === "string") {
- value = value.replace(/"/g, '""');
- }
+ strValue = strValue.replace(/"/g, '""');
// Wrap the value in double quotes to enclose any value that may
// have a, or a " in it to distinguish them from a comma-separated delimiter
- return `"${value}"`;
+ return `"${strValue}"`;
};
const convertToCSV = ({
diff --git a/frontend/utilities/debounce/index.ts b/frontend/utilities/debounce/index.ts
index e89f7e41796..d2036a4ddc0 100644
--- a/frontend/utilities/debounce/index.ts
+++ b/frontend/utilities/debounce/index.ts
@@ -8,8 +8,8 @@ interface IOptions {
const DEFAULT_TIMEOUT = 1000; // 1 function execution per second by default
-export default (
- func: (...args: any[]) => any,
+export default unknown>(
+ func: T,
options: IOptions = {
leading: true,
trailing: false,
diff --git a/frontend/utilities/deep_difference/index.ts b/frontend/utilities/deep_difference/index.ts
index 93504d19670..0212b6648f0 100644
--- a/frontend/utilities/deep_difference/index.ts
+++ b/frontend/utilities/deep_difference/index.ts
@@ -4,7 +4,9 @@ import { differenceWith, isArray, isEqual, isObject, map } from "lodash";
* Compares two objects and returns the differences.
* It returns the properties from obj1 that are different from obj2.
*/
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
const deepDifference = (obj1: any, obj2: any) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any = {};
map(obj1, (value, key) => {
diff --git a/frontend/utilities/format_api_errors/format_api_errors.ts b/frontend/utilities/format_api_errors/format_api_errors.ts
index a89b8c152ec..58cb10a1f07 100644
--- a/frontend/utilities/format_api_errors/format_api_errors.ts
+++ b/frontend/utilities/format_api_errors/format_api_errors.ts
@@ -1,12 +1,18 @@
-export default (error: any) => {
+interface IApiErrorResponse {
+ response?: {
+ errors?: Array<{ name: string; reason: string }>;
+ };
+}
+
+export default (error: IApiErrorResponse) => {
if (!error.response || !error.response.errors) {
return undefined;
}
const { errors: errorsArray } = error.response;
- const result: { [key: string]: any } = {};
+ const result: { [key: string]: string } = {};
- errorsArray.forEach((errorObject: any) => {
+ errorsArray.forEach((errorObject) => {
result[errorObject.name] = errorObject.reason;
});
diff --git a/frontend/utilities/format_error_response/index.ts b/frontend/utilities/format_error_response/index.ts
index f0abe8d3312..419ab46e0ad 100644
--- a/frontend/utilities/format_error_response/index.ts
+++ b/frontend/utilities/format_error_response/index.ts
@@ -21,15 +21,16 @@ const formatServerErrors = (errors: IFleetApiError[]) => {
return result; // TODO: Typing {base: string}
};
-const formatErrorResponse = (errorResponse: any) => {
+const formatErrorResponse = (errorResponse: unknown) => {
const errors =
get(errorResponse, "message.errors") ||
get(errorResponse, "data.errors") ||
[];
return {
- ...formatServerErrors(errors),
- http_status: errorResponse.status,
+ ...formatServerErrors(errors as IFleetApiError[]),
+ http_status: get(errorResponse, "status") as number | undefined,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any; // TODO: Fix type to IOldApiError
};
diff --git a/frontend/utilities/helpers.tsx b/frontend/utilities/helpers.tsx
index b7449699138..4a486e251a8 100644
--- a/frontend/utilities/helpers.tsx
+++ b/frontend/utilities/helpers.tsx
@@ -10,7 +10,6 @@ import {
trimEnd,
union,
uniqueId,
- upperFirst,
} from "lodash";
import md5 from "js-md5";
import {
@@ -62,7 +61,9 @@ import CustomLink from "components/CustomLink";
const ORG_INFO_ATTRS = ["org_name", "org_logo_url"];
const ADMIN_ATTRS = ["email", "name", "password", "password_confirmation"];
-export const addGravatarUrlToResource = (resource: any): any => {
+export const addGravatarUrlToResource = (
+ resource: T
+): T & { gravatar_url: string; gravatar_url_dark: string } => {
const { email } = resource;
const gravatarAvailable =
localStorage.getItem("gravatar_available") !== "false"; // Only fallback if explicitly set to "false"
@@ -172,17 +173,19 @@ export const formatFloatAsPercentage = (float?: number): string => {
return formatter.format(float);
};
-const formatLabelResponse = (response: any): ILabel[] => {
+const formatLabelResponse = (response: { labels: ILabel[] }): ILabel[] => {
const labels = response.labels.map((label: ILabel) => {
- let labelType = "custom";
+ let labelType: ILabel["type"] = "custom";
if (isPlatformLabelNameFromAPI(label.display_text)) {
- labelType = PLATFORM_LABEL_DISPLAY_TYPES[label.display_text];
+ labelType = PLATFORM_LABEL_DISPLAY_TYPES[
+ label.display_text
+ ] as ILabel["type"];
}
return {
...label,
slug: labelSlug(label),
type: labelType,
- target_type: "labels",
+ target_type: "labels" as const,
};
});
@@ -243,7 +246,7 @@ export const formatScheduledQueryForServer = (
}
if (queryID) {
- (result as any).report_id = Number(queryID);
+ (result as Record).report_id = Number(queryID);
}
if (shard) {
@@ -305,7 +308,7 @@ export const formatGlobalScheduledQueryForServer = (
}
if (queryID) {
- (result as any).report_id = Number(queryID);
+ (result as Record).report_id = Number(queryID);
}
if (shard) {
@@ -368,7 +371,7 @@ export const formatTeamScheduledQueryForServer = (
}
if (queryID) {
- (result as any).report_id = Number(queryID);
+ (result as Record).report_id = Number(queryID);
}
if (shard) {
@@ -376,7 +379,7 @@ export const formatTeamScheduledQueryForServer = (
}
if (teamID) {
- (result as any).fleet_id = Number(teamID);
+ (result as Record).fleet_id = Number(teamID);
}
return result;
@@ -764,7 +767,7 @@ export const abbreviateTimeUnits = (str: string): string =>
str.replace("minute", "min").replace("second", "sec");
// TODO: Type any because ts files missing the following properties from type 'JSON': parse, stringify, [Symbol.toStringTag]
-export const syntaxHighlight = (json: any): string => {
+export const syntaxHighlight = (json: unknown): string => {
let jsonStr: string = JSON.stringify(json, undefined, 2);
jsonStr = jsonStr
.replace(/&/g, "&")
@@ -843,11 +846,13 @@ export const normalizeEmptyValues = (
export const wait = (milliseconds: number) =>
new Promise((resolve) => setTimeout(resolve, milliseconds));
-export const wrapFleetHelper = (
- helperFn: (value: any) => string, // TODO: replace any with unknown and improve type narrowing by callers
- value: string
+export const wrapFleetHelper = (
+ helperFn: (value: T) => string,
+ value: T | string
): string => {
- return value === DEFAULT_EMPTY_CELL_VALUE ? value : helperFn(value);
+ return value === DEFAULT_EMPTY_CELL_VALUE
+ ? DEFAULT_EMPTY_CELL_VALUE
+ : helperFn(value as T);
};
interface ILocationParams {
diff --git a/frontend/utilities/scripts_encoding.tests.ts b/frontend/utilities/scripts_encoding.tests.ts
index 5bc9574ebd9..5dce70409a6 100644
--- a/frontend/utilities/scripts_encoding.tests.ts
+++ b/frontend/utilities/scripts_encoding.tests.ts
@@ -32,8 +32,9 @@ describe("scripts_encoding", () => {
it("should encode PowerShell install script patterns", () => {
const script = "$installProcess = Start-Process msiexec.exe";
const encoded = encodeScriptBase64(script);
+ expect(encoded).toBeDefined();
// Verify it's valid base64 and decodes back correctly
- expect(atob(encoded!)).toBe(script);
+ expect(atob(encoded as string)).toBe(script);
});
it("should encode multiline PowerShell scripts", () => {
@@ -41,15 +42,17 @@ describe("scripts_encoding", () => {
// eslint-disable-next-line no-template-curly-in-string
'$logFile = "${env:TEMP}/fleet-install.log"\nStart-Process msiexec.exe';
const encoded = encodeScriptBase64(script);
+ expect(encoded).toBeDefined();
// Verify it's valid base64 and decodes back correctly
- expect(atob(encoded!)).toBe(script);
+ expect(atob(encoded as string)).toBe(script);
});
it("should handle unicode characters correctly", () => {
const script = 'echo "Hello World"';
const encoded = encodeScriptBase64(script);
+ expect(encoded).toBeDefined();
// Decode and verify using TextDecoder for proper UTF-8 handling
- const decoded = atob(encoded!);
+ const decoded = atob(encoded as string);
expect(decoded).toBe(script);
});
@@ -72,9 +75,9 @@ describe("scripts_encoding", () => {
const encoded = encodeScriptBase64(script);
expect(encoded).toBeDefined();
// Verify it's valid base64 (won't throw)
- expect(() => atob(encoded!)).not.toThrow();
+ expect(() => atob(encoded as string)).not.toThrow();
// Verify it decodes back to original
- expect(atob(encoded!)).toBe(script);
+ expect(atob(encoded as string)).toBe(script);
});
});
});
diff --git a/frontend/utilities/simple_search/simple_search.ts b/frontend/utilities/simple_search/simple_search.ts
index 63eacf55e4f..43675b430f0 100644
--- a/frontend/utilities/simple_search/simple_search.ts
+++ b/frontend/utilities/simple_search/simple_search.ts
@@ -1,16 +1,12 @@
import { filter, includes } from "lodash";
-interface IDictionary {
- [key: string]: any;
-}
-
-const simpleSearch = (
+const simpleSearch = (
searchQuery = "",
- dictionary: IDictionary | undefined
-) => {
+ dictionary: T[] | undefined
+): T[] => {
const lowerSearchQuery = searchQuery.toLowerCase();
- const filterResults = filter(dictionary, (item) => {
+ const filterResults = filter(dictionary, (item: T) => {
if (!item.name) {
return false;
}
diff --git a/frontend/utilities/sort/sort_functions.ts b/frontend/utilities/sort/sort_functions.ts
index 692c86f8d5c..0dbc14f096f 100644
--- a/frontend/utilities/sort/sort_functions.ts
+++ b/frontend/utilities/sort/sort_functions.ts
@@ -8,14 +8,14 @@ const booleanAsc = (a: unknown, b: unknown): number => {
return 0;
};
-const caseInsensitiveAsc = (a: any, b: any): number => {
- a = typeof a === "string" ? a.toLowerCase() : a;
- b = typeof b === "string" ? b.toLowerCase() : b;
+const caseInsensitiveAsc = (a: unknown, b: unknown): number => {
+ const aVal = typeof a === "string" ? a.toLowerCase() : a;
+ const bVal = typeof b === "string" ? b.toLowerCase() : b;
- if (a < b) {
+ if ((aVal as string) < (bVal as string)) {
return -1;
}
- if (a > b) {
+ if ((aVal as string) > (bVal as string)) {
return 1;
}
return 0;
diff --git a/frontend/utilities/yaml/index.ts b/frontend/utilities/yaml/index.ts
index 6582c957029..96f2b4a4124 100644
--- a/frontend/utilities/yaml/index.ts
+++ b/frontend/utilities/yaml/index.ts
@@ -10,26 +10,30 @@ export const constructErrorString = (yamlError: IYAMLError) => {
return `${yamlError.name}: ${yamlError.reason} at line ${yamlError.line}`;
};
-export const agentOptionsToYaml = (agentOpts: any) => {
- agentOpts ||= { config: {} };
+export const agentOptionsToYaml = (agentOpts: unknown) => {
+ const opts: Record = (agentOpts as
+ | Record
+ | null
+ | undefined) ?? { config: {} };
// hide the "overrides" key if it is empty
- if (!agentOpts.overrides || Object.keys(agentOpts.overrides).length === 0) {
- delete agentOpts.overrides;
+ const overrides = opts.overrides as Record | undefined;
+ if (!overrides || Object.keys(overrides).length === 0) {
+ delete opts.overrides;
}
// add a comment besides the "command_line_flags" if it is empty
let addFlagsComment = false;
- if (
- !agentOpts.command_line_flags ||
- Object.keys(agentOpts.command_line_flags).length === 0
- ) {
+ const commandLineFlags = opts.command_line_flags as
+ | Record
+ | undefined;
+ if (!commandLineFlags || Object.keys(commandLineFlags).length === 0) {
// delete it so it does not render, and will add it explicitly after (along with the comment)
- delete agentOpts.command_line_flags;
+ delete opts.command_line_flags;
addFlagsComment = true;
}
- let yamlString = yaml.dump(agentOpts);
+ let yamlString = yaml.dump(opts);
if (addFlagsComment) {
yamlString +=
"# Requires Fleet's osquery installer\n# command_line_flags: {}\n";