fix: preserve app links and hover previews
This commit is contained in:
7 files changed
+47
-13
No files matched your search
@@ -14,7 +14,7 @@ import { useI18n } from "@homarr/translation/client";
|
||||
import { MaskedOrNormalImage } from "@homarr/ui";
|
||||
|
||||
import { WidgetEmptyState } from "../common/empty-state";
|
||||
import { getSafeApplicationUrl, SAFE_NEW_TAB_REL } from "../common/application-url";
|
||||
import { getSafeAppHref, SAFE_NEW_TAB_REL } from "../common/application-url";
|
||||
import { getUsableWidgetQueryData, isInitialWidgetQueryPending } from "../common/query-state";
|
||||
import { WidgetQueryErrorIndicator, WidgetQueryLoadingState } from "../common/query-state-indicator";
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
@@ -28,7 +28,7 @@ export default function AppWidget({ options, isEditMode, height, width }: Widget
|
||||
const board = useRequiredBoard();
|
||||
const appQuery = clientApi.app.byId.useQuery({ id: options.appId });
|
||||
const app = getUsableWidgetQueryData(appQuery);
|
||||
const href = getSafeApplicationUrl(app?.href);
|
||||
const href = getSafeAppHref(app?.href);
|
||||
useRegisterSpotlightContextResults(
|
||||
`app-${app?.id ?? options.appId}`,
|
||||
app && href
|
||||
|
||||
@@ -23,7 +23,7 @@ import { useRegisterSpotlightContextResults } from "@homarr/spotlight";
|
||||
import { MaskedOrNormalImage } from "@homarr/ui";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { getSafeApplicationUrl, SAFE_NEW_TAB_REL } from "../common/application-url";
|
||||
import { getSafeApplicationUrl, getSafeAppHref, SAFE_NEW_TAB_REL } from "../common/application-url";
|
||||
import { getUsableWidgetQueryData } from "../common/query-state";
|
||||
import classes from "./bookmark.module.css";
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function BookmarksWidget({
|
||||
useRegisterSpotlightContextResults(
|
||||
`bookmark-${itemId}`,
|
||||
data.flatMap((app) => {
|
||||
const href = getSafeApplicationUrl(app.href);
|
||||
const href = getSafeAppHref(app.href);
|
||||
return href
|
||||
? [
|
||||
{
|
||||
@@ -187,7 +187,7 @@ const FlexLayout = ({
|
||||
return (
|
||||
<Flex direction={direction} gap={4} w="100%" mih="100%" wrap="nowrap">
|
||||
{data.map((app) => {
|
||||
const href = getSafeApplicationUrl(app.href);
|
||||
const href = getSafeAppHref(app.href);
|
||||
return (
|
||||
<div key={app.id} style={{ display: "flex", flex: `1 0 ${minimumItemSize}px`, flexDirection: direction }}>
|
||||
<UnstyledButton
|
||||
@@ -263,7 +263,7 @@ const AdvancedBookmarksLayout = ({
|
||||
<ScrollArea h="100%" style={{ flex: 1 }}>
|
||||
<SimpleGrid cols={getAdvancedBookmarkColumns(width, data.length)} spacing="sm">
|
||||
{data.map((app) => {
|
||||
const href = getSafeApplicationUrl(app.href);
|
||||
const href = getSafeAppHref(app.href);
|
||||
return (
|
||||
<UnstyledButton
|
||||
key={app.id}
|
||||
@@ -342,7 +342,7 @@ const GridLayout = ({
|
||||
style={{ gridAutoRows: `minmax(${minimumItemHeight}px, 1fr)` }}
|
||||
>
|
||||
{data.map((app) => {
|
||||
const href = getSafeApplicationUrl(app.href);
|
||||
const href = getSafeAppHref(app.href);
|
||||
return (
|
||||
<UnstyledButton
|
||||
className={classes.bookmarkButton}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { getSafeApplicationUrl } from "./application-url";
|
||||
import { getSafeApplicationUrl, getSafeAppHref } from "./application-url";
|
||||
|
||||
describe("getSafeApplicationUrl", () => {
|
||||
test.each(["javascript:alert(1)", "data:text/html,test", "not a URL", "/relative", "./relative"])(
|
||||
@@ -28,3 +28,19 @@ describe("getSafeApplicationUrl", () => {
|
||||
expect(getSafeApplicationUrl(value)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSafeAppHref", () => {
|
||||
test.each(["#section", "/boards/home", "./relative", "vscode://file/example.ts", "https://example.com/path"])(
|
||||
"allows configured app link %s",
|
||||
(value) => expect(getSafeAppHref(value)).toBe(value),
|
||||
);
|
||||
|
||||
test.each([
|
||||
"javascript:alert(1)",
|
||||
"data:text/html,test",
|
||||
"file:///etc/passwd",
|
||||
"//example.com/path",
|
||||
"not a URL",
|
||||
"https://user:password@example.com/path",
|
||||
])("rejects unsafe app link %s", (value) => expect(getSafeAppHref(value)).toBeUndefined());
|
||||
});
|
||||
@@ -1 +1 @@
|
||||
export { getSafeApplicationUrl, SAFE_NEW_TAB_REL } from "@homarr/common";
|
||||
export { getSafeApplicationUrl, getSafeAppHref, SAFE_NEW_TAB_REL } from "@homarr/common";
|
||||
Reference in new issue
Block a user