Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
149 changes: 123 additions & 26 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
"react-hot-toast": "^2.2.0",
"react-icons": "^5.0.0",
"react-image": "^4.1.0",
"react-joyride": "^3.1.0",
"react-loading-icons": "^1.0.8",
"react-mentions": "^4.4.0",
"react-responsive": "^9.0.2",
Expand Down
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
} from "./context/UserAccessContext/UserAccessContext";
import { tables } from "./context/UserAccessContext/permissions";

import { GettingStartedPage } from "./pages/GettingStartedPage";
import { ArtifactsPage } from "./pages/Settings/ArtifactsPage";
import { PermissionsPage } from "./pages/Settings/PermissionsPage";
import { PermissionsSubjectsPage } from "./pages/Settings/PermissionsSubjectsPage";
Expand Down Expand Up @@ -719,6 +720,10 @@ export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) {
/>
</Route>

<Route path="getting-started" element={sidebar}>
<Route index element={<GettingStartedPage />} />
</Route>

<Route path="views" element={sidebar}>
<Route
path=":id"
Expand Down
25 changes: 25 additions & 0 deletions src/api/services/touchpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ABOUTME: Reads and records person_touchpoints — the checklist actions a user has completed.
// ABOUTME: Upserts on (person_id, key) via merge-duplicates so re-doing an action is idempotent.
import { IncidentCommander } from "../axios";
import { resolvePostGrestRequestWithPagination } from "../resolve";

export type PersonTouchpoint = {
person_id: string;
key: string;
};

export function fetchPersonTouchpoints(personId: string) {
return resolvePostGrestRequestWithPagination<Pick<PersonTouchpoint, "key">[]>(
IncidentCommander.get(
`/person_touchpoints?person_id=eq.${personId}&select=key`
)
);
}

export function recordPersonTouchpoint(personId: string, key: string) {
return IncidentCommander.post(
"/person_touchpoints",
{ person_id: personId, key },
{ headers: { Prefer: "resolution=merge-duplicates,return=minimal" } }
);
}
10 changes: 10 additions & 0 deletions src/components/Authentication/Kratos/KratosUserProfileDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type UserProfileDropdownProps = {
openCliSetupModal: () => void;
openResourceSelectorSearchModal: () => void;
openScopeImpersonationModal: () => void;
openGettingStarted: () => void;
showScopeImpersonation?: boolean;
};

Expand All @@ -32,6 +33,7 @@ export function KratosUserProfileDropdown({
openCliSetupModal,
openResourceSelectorSearchModal,
openScopeImpersonationModal,
openGettingStarted,
showScopeImpersonation = false
}: UserProfileDropdownProps) {
const { user } = useUser();
Expand Down Expand Up @@ -93,6 +95,14 @@ export function KratosUserProfileDropdown({
</a>
</MenuItem>
))}
<MenuItem>
<button
onClick={openGettingStarted}
className="block border-0 border-b border-gray-200 px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900"
>
Getting started
</button>
</MenuItem>
<MenuItem>
<button
onClick={openKubeConfigModal}
Expand Down
10 changes: 5 additions & 5 deletions src/components/Authentication/Kratos/ory/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export function handleGetFlowError<S>(
resetFlow: Dispatch<SetStateAction<S | undefined>>
) {
return async (err: AxiosError<any>) => {
switch (err.response?.data.error?.id) {
switch (err.response?.data?.error?.id) {
case "session_inactive":
await router.push("/login?return_to=" + window.location.href);
return;
case "session_aal2_required":
if (err.response?.data.redirect_browser_to) {
const redirectTo = new URL(err.response?.data.redirect_browser_to);
if (err.response?.data?.redirect_browser_to) {
const redirectTo = new URL(err.response?.data?.redirect_browser_to);
if (flowType === "settings") {
redirectTo.searchParams.set("return_to", window.location.href);
}
Expand Down Expand Up @@ -51,7 +51,7 @@ export function handleGetFlowError<S>(
}
case "session_refresh_required":
// We need to re-authenticate to perform this action
window.location.href = err.response?.data.redirect_browser_to;
window.location.href = err.response?.data?.redirect_browser_to;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard redirect_browser_to before assigning window.location.href

Both branches now allow undefined to flow into window.location.href. If Kratos returns a partial payload, this can trigger incorrect navigation rather than a controlled fallback. Add a null check (and fallback route/reset) before redirecting.

Suggested fix
       case "session_refresh_required":
         // We need to re-authenticate to perform this action
-        window.location.href = err.response?.data?.redirect_browser_to;
+        if (err.response?.data?.redirect_browser_to) {
+          window.location.href = err.response.data.redirect_browser_to;
+          return;
+        }
+        resetFlow(undefined);
+        await router.push("/" + flowType);
         return;
@@
       case "browser_location_change_required":
         // Ory Kratos asked us to point the user to this URL.
-        window.location.href = err.response?.data?.redirect_browser_to;
+        if (err.response?.data?.redirect_browser_to) {
+          window.location.href = err.response.data.redirect_browser_to;
+          return;
+        }
+        resetFlow(undefined);
+        await router.push("/" + flowType);
         return;

Also applies to: 85-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Authentication/Kratos/ory/hooks.ts` at line 54, The code
assigns err.response?.data?.redirect_browser_to directly to window.location.href
without verifying that redirect_browser_to is actually defined. If Kratos
returns an incomplete response where redirect_browser_to is undefined, this will
cause uncontrolled navigation. Add a guard condition to check if
redirect_browser_to exists before assigning it to window.location.href, and
implement a controlled fallback behavior (such as redirecting to a safe route or
resetting the state) when it is undefined. This same fix needs to be applied to
both instances mentioned (line 54 and line 85).

return;
case "self_service_flow_return_to_forbidden":
// The flow expired, let's request a new one.
Expand Down Expand Up @@ -82,7 +82,7 @@ export function handleGetFlowError<S>(
return;
case "browser_location_change_required":
// Ory Kratos asked us to point the user to this URL.
window.location.href = err.response.data.redirect_browser_to;
window.location.href = err.response?.data?.redirect_browser_to;
return;
}

Expand Down
29 changes: 22 additions & 7 deletions src/components/Canary/CanaryPopup/CheckDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { DropdownStandaloneWrapper } from "../../Dropdown/StandaloneWrapper";
import { TimeRange, timeRanges } from "../../Dropdown/TimeRange";
import { refreshCheckModalAtomTrigger } from "../ChecksListing";
import { Duration } from "../renderers";
import { RecordTouchpointOnMount } from "@flanksource-ui/components/GuidedTour/TouchpointObserver";
import { CanaryCheckDetailsSpecTab } from "./CanaryCheckDetailsSpec";
import { CheckDetailsTabs } from "./CheckDetailsTabs";
import CheckLabels from "./CheckLabels";
Expand Down Expand Up @@ -118,7 +119,7 @@ export function CheckDetails({ check, ...rest }: CheckDetailsProps) {
<div {...rest} ref={containerRef}>
<div className="flex flex-col" ref={statsRef}>
<CheckLabels check={check} />
<div className="flex flex-row flex-wrap">
<div data-tour="check-stats" className="flex flex-row flex-wrap">
<Stat
containerClassName="w-44 mb-4"
title="Uptime"
Expand Down Expand Up @@ -240,19 +241,33 @@ export function CheckDetails({ check, ...rest }: CheckDetailsProps) {
tabs={{
statusHistory: {
label: "History",
content: <StatusHistory timeRange={timeRange} check={check} />,
content: (
<StatusHistory
data-tour="check-timeline"
timeRange={timeRange}
check={check}
/>
),
class: `flex-1 flex flex-col h-full`
},
graph: {
label: "Graph",
hidden: !isShort,
content: (
<Short>
<CanaryChart
check={check}
timeRange={timeRange}
height="h-full"
/>
<>
<RecordTouchpointOnMount id="health.view-graph" />
<div
data-tour="check-graph"
className="flex h-full w-full flex-col"
>
<CanaryChart
check={check}
timeRange={timeRange}
height="h-full"
/>
</div>
</>
</Short>
),
class: `flex-1 flex flex-col overflow-y-auto ${mixins.appleScrollbar}`
Expand Down
Loading
Loading