CVE-2022-4683 (Other, published 2022-12-23T12:15Z)
Description: value: Sensitive Cookie in HTTPS Session Without 'Secure' Attribute in GitHub repository usememos/memos prior to 0.9.0.
Severity: nan (CVSS v3 base score: 6.5)
Weakness: CWE-311 \u2014 Missing Encryption of Sensitive Data
The product does not encrypt sensitive or critical information before storage or transmission.
Repository: https://github.com/usememos/memos
Files changed:
api/system_setting.go
api/user_setting.go
go.mod
go.sum
server/acl.go
server/system.go
web/src/components/AppearanceSelect.tsx
web/src/components/LocaleSelect.tsx
web/src/components/Settings/PreferencesSection.tsx
web/src/components/UpdateCustomizedProfileDialog.tsx
web/src/pages/Auth.tsx
web/src/store/module/global.ts
web/src/store/reducer/global.ts
web/src/types/modules/system.d.ts
Commit message: feat: customize system profile (#828)



Vulnerable code:
		value := CustomizedProfile{
		err := json.Unmarshal([]byte(upsert.Value), &value)

		invalid := true
		for _, value := range UserSettingLocaleValue {
			if localeValue == value {
				invalid = false
				break
			}
		}
		if invalid {
		appearanceValue := "light"

		invalid := true
		for _, value := range UserSettingAppearanceValue {
			if appearanceValue == value {
				invalid = false
				break
			}
		}
		if invalid {

		invalid := true
		for _, value := range UserSettingMemoVisibilityValue {
			if memoVisibilityValue == value {
				invalid = false
				break
			}
		}
		if invalid {

		invalid := true
		for _, value := range UserSettingMemoDisplayTsOptionKeyValue {
			if memoDisplayTsOption == value {
				invalid = false
				break
			}
		}
		if invalid {
	golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
require github.com/segmentio/analytics-go v3.1.0+incompatible
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
				Name: "memos",
import { useGlobalStore, useUserStore } from "../store/module";
const AppearanceSelect = () => {
  const globalStore = useGlobalStore();
  const userStore = useUserStore();
  const { appearance } = globalStore.state;
  const user = userStore.state.user;
    if (user) {
      await userStore.upsertUserSetting("appearance", appearance);
    }
    globalStore.setAppearance(appearance);
      className="!min-w-[10rem] w-auto text-sm"
      value={appearance}
      startDecorator={getPrefixIcon(appearance)}
import Icon from "../Icon";
const localeSelectorItems = [
  {
    text: "English",
    value: "en",
  },
  {
    text: "中文",
    value: "zh",
  },
  {
    text: "Tiếng Việt",
    value: "vi",
  },
  {
    text: "French",
    value: "fr",
  },
  {
    text: "Nederlands",
    value: "nl",
  },
  {
    text: "Svenska",
    value: "sv",
  },
  {
    text: "German",
    value: "de",
  },
  {
    text: "Español",
    value: "es",
  },
];

  const handleLocaleChanged = async (value: string) => {
    await userStore.upsertUserSetting("locale", value);
    globalStore.setLocale(value as Locale);
        <Select
          className="!min-w-[10rem] w-auto text-sm"
          value={setting.locale}
          onChange={(_, locale) => {
            if (locale) {
              handleLocaleChanged(locale);
            }
          }}
          startDecorator={<Icon.Globe className="w-4 h-auto" />}
        >
          {localeSelectorItems.map((item) => (
            <Option key={item.value} value={item.value} className="whitespace-nowrap">
              {item.text}
            </Option>
          ))}
        </Select>
        <AppearanceSelect />
      <div className="dialog-header-container !w-64">
      <div className="dialog-content-container">
import { Option, Select } from "@mui/joy";
  const { t, i18n } = useTranslation();
  const systemStatus = globalStore.state.systemStatus;
  const handleLocaleItemClick = (locale: Locale) => {
    globalStore.setLocale(locale);
  };

            <p className="slogan-text">{t("slogan")}</p>
          <Select
            className="!min-w-[9rem] w-auto whitespace-nowrap"
            startDecorator={<Icon.Globe className="w-4 h-auto" />}
            value={i18n.language}
            onChange={(_, value) => handleLocaleItemClick(value as Locale)}
          >
            <Option value="en">English</Option>
            <Option value="zh">中文</Option>
            <Option value="vi">Tiếng Việt</Option>
            <Option value="fr">French</Option>
            <Option value="nl">Nederlands</Option>
            <Option value="sv">Svenska</Option>
            <Option value="de">German</Option>
            <Option value="es">Español</Option>
          </Select>
          <AppearanceSelect />
        iconUrl: "/logo.webp",
        iconUrl: "/logo.webp",

Fixed code:

	"golang.org/x/exp/slices"
	// Description is the server description.
	Description string `json:"description"`
	// Locale is the server default locale.
	Locale string `json:"locale"`
	// Appearance is the server default appearance.
	Appearance string `json:"appearance"`
		customizedProfile := CustomizedProfile{
			Description: "",
			Locale:      "en",
			Appearance:  "system",
		err := json.Unmarshal([]byte(upsert.Value), &customizedProfile)
		if !slices.Contains(UserSettingLocaleValue, customizedProfile.Locale) {
			return fmt.Errorf("invalid locale value")
		}
		if !slices.Contains(UserSettingAppearanceValue, customizedProfile.Appearance) {
			return fmt.Errorf("invalid appearance value")
		}

	"golang.org/x/exp/slices"
		if !slices.Contains(UserSettingLocaleValue, localeValue) {
		appearanceValue := "system"
		if !slices.Contains(UserSettingAppearanceValue, appearanceValue) {
		if !slices.Contains(UserSettingMemoVisibilityValue, memoVisibilityValue) {
		if !slices.Contains(UserSettingMemoDisplayTsOptionKeyValue, memoDisplayTsOption) {
	golang.org/x/sys v0.1.0 // indirect
require (
	github.com/segmentio/analytics-go v3.1.0+incompatible
	golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15
)
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 h1:5oN1Pz/eDhCpbMbLstvIPa0b/BEQo6g6nwV3pLjfM6w=
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
		Secure:   true,
				Name:        "memos",
				IconURL:     "",
				Description: "",
				Locale:      "en",
				Appearance:  "system",
				ExternalURL: "",
					Description: valueMap["description"].(string),
					Locale:      valueMap["locale"].(string),
					Appearance:  valueMap["appearance"].(string),
import { FC } from "react";
interface Props {
  value: Appearance;
  onChange: (appearance: Appearance) => void;
  className?: string;
}

const AppearanceSelect: FC<Props> = (props: Props) => {
  const { onChange, value, className } = props;
    onChange(appearance);
      className={`!min-w-[10rem] w-auto whitespace-nowrap ${className ?? ""}`}
      value={value}
      startDecorator={getPrefixIcon(value)}
import { Option, Select } from "@mui/joy";
import { FC } from "react";
import Icon from "./Icon";

interface Props {
  value: Locale;
  onChange: (locale: Locale) => void;
  className?: string;
}

const LocaleSelect: FC<Props> = (props: Props) => {
  const { onChange, value, className } = props;

  const handleSelectChange = async (locale: Locale) => {
    onChange(locale);
  };

  return (
    <Select
      className={`!min-w-[10rem] w-auto whitespace-nowrap ${className ?? ""}`}
      startDecorator={<Icon.Globe className="w-4 h-auto" />}
      value={value}
      onChange={(_, value) => handleSelectChange(value as Locale)}
    >
      <Option value="en">English</Option>
      <Option value="zh">中文</Option>
      <Option value="vi">Tiếng Việt</Option>
      <Option value="fr">French</Option>
      <Option value="nl">Nederlands</Option>
      <Option value="sv">Svenska</Option>
      <Option value="de">German</Option>
      <Option value="es">Español</Option>
    </Select>
  );
};

export default LocaleSelect;
import LocaleSelect from "../LocaleSelect";
  const { appearance, locale } = globalStore.state;
  const handleLocaleSelectChange = async (locale: Locale) => {
    await userStore.upsertUserSetting("locale", locale);
    globalStore.setLocale(locale);
  };

  const handleAppearanceSelectChange = async (appearance: Appearance) => {
    await userStore.upsertUserSetting("appearance", appearance);
    globalStore.setAppearance(appearance);
        <LocaleSelect value={locale} onChange={handleLocaleSelectChange} />
        <AppearanceSelect value={appearance} onChange={handleAppearanceSelectChange} />
import LocaleSelect from "./LocaleSelect";
import AppearanceSelect from "./AppearanceSelect";
  const handleDescriptionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
    setState((state) => {
      return {
        ...state,
        description: e.target.value as string,
      };
    });
  };

  const handleLocaleSelectChange = (locale: Locale) => {
    setState((state) => {
      return {
        ...state,
        locale: locale,
      };
    });
  };

  const handleAppearanceSelectChange = (appearance: Appearance) => {
    setState((state) => {
      return {
        ...state,
        appearance: appearance,
      };
    });
  };

      <div className="dialog-header-container">
      <div className="dialog-content-container !w-80">
        <p className="text-sm mb-1 mt-2">Description</p>
        <input type="text" className="input-text" value={state.description} onChange={handleDescriptionChanged} />
        <p className="text-sm mb-1 mt-2">Server locale</p>
        <LocaleSelect className="w-full" value={state.locale} onChange={handleLocaleSelectChange} />
        <p className="text-sm mb-1 mt-2">Server appearance</p>
        <AppearanceSelect className="w-full" value={state.appearance} onChange={handleAppearanceSelectChange} />
import LocaleSelect from "../components/LocaleSelect";
  const { t } = useTranslation();
  const { appearance, locale, systemStatus } = globalStore.state;
  const handleLocaleSelectChange = (locale: Locale) => {
    globalStore.setLocale(locale);
  };

  const handleAppearanceSelectChange = (appearance: Appearance) => {
    globalStore.setAppearance(appearance);
  };

            <p className="slogan-text">{systemStatus.customizedProfile.description || t("slogan")}</p>
          <LocaleSelect value={locale} onChange={handleLocaleSelectChange} />
          <AppearanceSelect value={appearance} onChange={handleAppearanceSelectChange} />
        iconUrl: "https://usememos.com/logo.webp",
        description: "",
        locale: "en",
        appearance: "system",
      defaultGlobalState.locale = data.customizedProfile.locale;
      defaultGlobalState.appearance = data.customizedProfile.appearance;
        iconUrl: "https://usememos.com/logo.webp",
        description: "",
        locale: "en",
        appearance: "system",
  description: string;
  locale: Locale;
  appearance: Appearance;