diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c1bdf234a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions +Only the following versions will receive updates, that include improvements to the security: + +| Version | Supported | +| ------- | ------------------ | +| 0.13 | :white_check_mark: | +| <=0.12 | :x: | + +## Reporting a Vulnerability +We take security issues very seriously. +When you found a security issue, please ask yourself the following question: + +**Would this be publicly disclosed, could it cause any problems or harm to any Homarr instances or individuals?** + +If the answer to that question is yes, please contact us immideatly using https://homarr.dev/docs/community/get-in-touch. E-Mail is preferred, but you can write ``manicraft1001`` or ``ajnart`` on Discord as well. +If the answer is no, please create a public visible issue: https://github.com/ajnart/homarr/issues/new?assignees=&labels=%F0%9F%90%9B+Bug&projects=&template=bug.yml&title=[Vulnerability] diff --git a/package.json b/package.json index b2ac01ced..5d1a7761a 100644 --- a/package.json +++ b/package.json @@ -220,4 +220,4 @@ ] } } -} +} \ No newline at end of file diff --git a/public/locales/en/layout/modals/add-app.json b/public/locales/en/layout/modals/add-app.json index 551f84dad..bed0d0630 100644 --- a/public/locales/en/layout/modals/add-app.json +++ b/public/locales/en/layout/modals/add-app.json @@ -24,7 +24,8 @@ "isOpeningNewTab": { "label": "Open in new tab", "description": "Open the app in a new tab instead of the current one." - } + }, + "customProtocolWarning": "Using a non-standard protocol. This may require pre-installed applications and can introduce security risks. Ensure that your address is secure and trusted." }, "network": { "statusChecker": { diff --git a/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx b/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx index c2dfa21f5..a618f6d2e 100644 --- a/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/EditAppModal.tsx @@ -28,6 +28,9 @@ import { EditAppModalTab } from './Tabs/type'; const appUrlRegex = '(https?://(?:www.|(?!www))\\[?[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\]?.[^\\s]{2,}|www.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9].[^\\s]{2,}|https?://(?:www.|(?!www))\\[?[a-zA-Z0-9]+\\]?.[^\\s]{2,}|www.[a-zA-Z0-9]+.[^\\s]{2,})'; +const appUrlWithAnyProtocolRegex = + '([A-z]+://(?:www.|(?!www))\\[?[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\]?.[^\\s]{2,}|www.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9].[^\\s]{2,}|[A-z]+://(?:www.|(?!www))\\[?[a-zA-Z0-9]+\\]?.[^\\s]{2,}|www.[a-zA-Z0-9]+.[^\\s]{2,})'; + export const EditAppModal = ({ context, id, @@ -71,8 +74,8 @@ export const EditAppModal = ({ return null; } - if (!url.match(appUrlRegex)) { - return 'Uri override is not a valid uri'; + if (!url.match(appUrlWithAnyProtocolRegex)) { + return 'External URI is not a valid uri'; } return null; diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx index fd5c2ea05..6bbc40a7b 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx @@ -1,4 +1,4 @@ -import { Tabs, TextInput } from '@mantine/core'; +import { Tabs, Text, TextInput } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; import { IconClick, IconCursorText, IconLink } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; @@ -22,6 +22,7 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => { placeholder="My example app" variant="default" withAsterisk + mb="md" {...form.getInputProps('name')} /> { placeholder="https://google.com" variant="default" withAsterisk + mb="md" {...form.getInputProps('url')} - onChange={(e) => { - form.setFieldValue('url', e.target.value); - }} /> } @@ -44,6 +43,13 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => { variant="default" {...form.getInputProps('behaviour.externalUrl')} /> + + {!form.values.behaviour.externalUrl.startsWith('https://') && + !form.values.behaviour.externalUrl.startsWith('http://') && ( + + {t('behaviour.customProtocolWarning')} + + )} ); }; diff --git a/src/config/provider.tsx b/src/config/provider.tsx index 1d6803b17..d0002b67d 100644 --- a/src/config/provider.tsx +++ b/src/config/provider.tsx @@ -21,8 +21,16 @@ const ConfigContext = createContext({ setConfigName: () => {}, }); -export const ConfigProvider = ({ children }: { children: ReactNode }) => { - const [configName, setConfigName] = useState(); +export const ConfigProvider = ({ + children, + config: fallbackConfig, + configName: initialConfigName, +}: { + children: ReactNode; + config?: ConfigType; + configName?: string; +}) => { + const [configName, setConfigName] = useState(initialConfigName || 'default'); const [configVersion, setConfigVersion] = useState(0); const { configs } = useConfigStore((s) => ({ configs: s.configs }), shallow); const { setPrimaryColor, setSecondaryColor, setPrimaryShade } = useColorTheme(); @@ -39,7 +47,7 @@ export const ConfigProvider = ({ children }: { children: ReactNode }) => { setConfigVersion((v) => v + 1), setConfigName: (name: string) => setConfigName(name), diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3a21f1960..681bdff42 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -36,6 +36,7 @@ import { getServiceSidePackageAttributes, } from '../tools/server/getPackageVersion'; import { theme } from '../tools/server/theme/theme'; +import { ConfigType } from '~/types/config'; function App( this: any, @@ -44,13 +45,15 @@ function App( packageAttributes: ServerSidePackageAttributesType; editModeEnabled: boolean; defaultColorScheme: ColorScheme; + config?: ConfigType; + configName?: string; }> ) { const { Component, pageProps } = props; - const [primaryColor, setPrimaryColor] = useState('red'); - const [secondaryColor, setSecondaryColor] = useState('orange'); - const [primaryShade, setPrimaryShade] = useState(6); + const [primaryColor, setPrimaryColor] = useState(props.pageProps.config?.settings.customization.colors.primary || 'red'); + const [secondaryColor, setSecondaryColor] = useState(props.pageProps.config?.settings.customization.colors.secondary || 'orange'); + const [primaryShade, setPrimaryShade] = useState(props.pageProps.config?.settings.customization.colors.shade || 6); const colorTheme = { primaryColor, secondaryColor, @@ -124,7 +127,7 @@ function App( withGlobalStyles withNormalizeCSS > - +