Skip to content

fix: add checks for preventing HSL colors from entering React state #9893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: add extra runtime validation for colors
  • Loading branch information
Parkreiner committed Sep 27, 2023
commit 115813e6f25da2259cb76b5bdf1caa3446778c47
36 changes: 33 additions & 3 deletions site/src/components/Dashboard/DashboardProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ import {
} from "api/typesGenerated";
import { FullScreenLoader } from "components/Loader/FullScreenLoader";
import {
type FC,
type PropsWithChildren,
createContext,
FC,
PropsWithChildren,
useCallback,
useContext,
useState,
} from "react";
import { appearance } from "api/queries/appearance";
import { hslToHex, isHexColor, isHslColor } from "utils/colors";
import { displayError } from "components/GlobalSnackbar/utils";

interface Appearance {
config: AppearanceConfig;
Expand Down Expand Up @@ -45,8 +48,35 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
!entitlementsQuery.data ||
!appearanceQuery.data ||
!experimentsQuery.data;

const [configPreview, setConfigPreview] = useState<AppearanceConfig>();

// Centralizing the logic for catching malformed configs in one spot, just to
// be on the safe side; don't want to expose raw setConfigPreview outside
// the provider
const setPreview = useCallback((newConfig: AppearanceConfig) => {
// Have runtime safety nets in place, just because so much of the codebase
// relies on HSL for formatting, but server expects hex values. Can't catch
// color format mismatches at the type level
const incomingBg = newConfig.service_banner.background_color;
let configForDispatch = newConfig;

if (typeof incomingBg === "string" && isHslColor(incomingBg)) {
configForDispatch = {
...newConfig,
service_banner: {
...newConfig.service_banner,
background_color: hslToHex(incomingBg),
},
};
} else if (typeof incomingBg === "string" && !isHexColor(incomingBg)) {
displayError(`The value ${incomingBg} is not a valid hex string`);
return;
}

setConfigPreview(configForDispatch);
}, []);

if (isLoading) {
return <FullScreenLoader />;
}
Expand All @@ -59,7 +89,7 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
experiments: experimentsQuery.data,
appearance: {
config: configPreview ?? appearanceQuery.data,
setPreview: setConfigPreview,
setPreview: setPreview,
isPreview: configPreview !== undefined,
},
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,17 @@ const AppearanceSettingsPage: FC = () => {
const { appearance, entitlements } = useDashboard();
const queryClient = useQueryClient();
const updateAppearanceMutation = useMutation(updateAppearance(queryClient));
const isEntitled =
entitlements.features["appearance"].entitlement !== "not_entitled";

const onSaveAppearance = async (
newConfig: Partial<UpdateAppearanceConfig>,
preview: boolean,
) => {
const newAppearance = {
...appearance.config,
...newConfig,
};
const newAppearance = { ...appearance.config, ...newConfig };
if (preview) {
appearance.setPreview(newAppearance);
return;
}

try {
await updateAppearanceMutation.mutateAsync(newAppearance);
displaySuccess("Successfully updated appearance settings!");
Expand All @@ -50,8 +46,10 @@ const AppearanceSettingsPage: FC = () => {

<AppearanceSettingsPageView
appearance={appearance.config}
isEntitled={isEntitled}
onSaveAppearance={onSaveAppearance}
isEntitled={
entitlements.features.appearance.entitlement !== "not_entitled"
}
/>
</>
);
Expand Down