From 032e6d968c17abf37e5e8a44db296c6a83d41387 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 3 Oct 2022 13:36:28 -0300 Subject: [PATCH] Validate ClusterModel before trying to update or create a Cluster (#4515) * Validate ClusterModel before trying to update or create a Cluster Signed-off-by: Sebastian Malton * Fix tests Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- src/common/cluster-types.ts | 33 +++++++++++++++++++++++++++++++++ src/common/cluster/cluster.ts | 19 ++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/common/cluster-types.ts b/src/common/cluster-types.ts index f0862e07b8..faf6debbab 100644 --- a/src/common/cluster-types.ts +++ b/src/common/cluster-types.ts @@ -3,6 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import Joi from "joi"; + /** * JSON serializable metadata type */ @@ -27,6 +29,37 @@ export type ClusterId = string; */ export type UpdateClusterModel = Omit; +/** + * A type validator for `UpdateClusterModel` so that only expected types are present + */ +export const updateClusterModelChecker = Joi.object({ + kubeConfigPath: Joi.string() + .required() + .min(1), + contextName: Joi.string() + .required() + .min(1), + workspace: Joi.string() + .optional(), + workspaces: Joi.array() + .items(Joi.string()), + preferences: Joi.object(), + metadata: Joi.object(), + accessibleNamespaces: Joi.array() + .items(Joi.string()), + labels: Joi.object().pattern(Joi.string(), Joi.string()), +}); + +/** + * A type validator for just the `id` fields of `ClusterModel`. The rest is + * covered by `updateClusterModelChecker` + */ +export const clusterModelIdChecker = Joi.object>({ + id: Joi.string() + .required() + .min(1), +}); + /** * The model for passing cluster data around, including to disk */ diff --git a/src/common/cluster/cluster.ts b/src/common/cluster/cluster.ts index f1a943732c..9b0eed76aa 100644 --- a/src/common/cluster/cluster.ts +++ b/src/common/cluster/cluster.ts @@ -16,7 +16,7 @@ import type { VersionDetector } from "../../main/cluster-detectors/version-detec import type { DetectorRegistry } from "../../main/cluster-detectors/detector-registry"; import plimit from "p-limit"; import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate, ClusterConfigData } from "../cluster-types"; -import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../cluster-types"; +import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus, clusterModelIdChecker, updateClusterModelChecker } from "../cluster-types"; import { disposer, isDefined, isRequestError, toJS } from "../utils"; import type { Response } from "request"; import { clusterListNamespaceForbiddenChannel } from "../ipc/cluster"; @@ -237,9 +237,16 @@ export class Cluster implements ClusterModel, ClusterState { return this.preferences.defaultNamespace; } - constructor(private readonly dependencies: ClusterDependencies, model: ClusterModel, configData: ClusterConfigData) { + constructor(private readonly dependencies: ClusterDependencies, { id, ...model }: ClusterModel, configData: ClusterConfigData) { makeObservable(this); - this.id = model.id; + + const { error } = clusterModelIdChecker.validate({ id }); + + if (error) { + throw error; + } + + this.id = id; this.updateModel(model); this.apiUrl = configData.clusterServerUrl; @@ -261,6 +268,12 @@ export class Cluster implements ClusterModel, ClusterState { @action updateModel(model: UpdateClusterModel) { // Note: do not assign ID as that should never be updated + const { error } = updateClusterModelChecker.validate(model, { allowUnknown: true }); + + if (error) { + throw error; + } + this.kubeConfigPath = model.kubeConfigPath; this.contextName = model.contextName;