fix: key-detail not-found state, bound SPA fallback probe, drop stale deprecation

Addresses Greptile review: KeyDetailPage now renders an explicit 'Key not found' page (with a back button) when the id in the URL resolves to no key, instead of leaning on KeyInfoView's internal fallback. SPAStaticFiles._spa_shell now resolves each candidate shell and rejects anything outside the static directory before probing it with os.path.isfile, so a '..'-bearing URL can't make the existence check escape the served directory. The @deprecated notice on templates/key_info_view.tsx is removed: it pointed at a successor file that does not exist and the component is the sole implementation used across the dashboard
This commit is contained in:
ryan-crabbe-berri 2026-07-09 22:39:07 -07:00
parent 115694dc0e
commit b3b2ec5d58
3 changed files with 26 additions and 20 deletions

View file

@ -620,6 +620,13 @@ class SPAStaticFiles(StaticFiles):
last_segment = path.rstrip("/").rsplit("/", 1)[-1]
return "." not in last_segment
def _bounded_shell(self, relative: str) -> Optional[str]:
base = os.path.realpath(str(self.directory))
candidate = os.path.realpath(os.path.join(base, relative))
if candidate != base and not candidate.startswith(base + os.sep):
return None
return relative if os.path.isfile(candidate) else None
def _spa_shell(self, path: str) -> Optional[str]:
trimmed = path.strip("/")
segments = trimmed.split("/") if trimmed else []
@ -630,11 +637,10 @@ class SPAStaticFiles(StaticFiles):
if parent
else f"{_SPA_SHELL_PLACEHOLDER_SEGMENT}/index.html"
)
if os.path.isfile(os.path.join(str(self.directory), placeholder)):
return placeholder
if os.path.isfile(os.path.join(str(self.directory), "index.html")):
return "index.html"
return None
shell = self._bounded_shell(placeholder)
if shell is not None:
return shell
return self._bounded_shell("index.html")
async def _try_original(self, path: str, scope: StarletteScope) -> Optional[StarletteResponse]:
try:

View file

@ -6,6 +6,8 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import LoadingScreen from "@/components/common_components/LoadingScreen";
import KeyInfoView from "@/components/templates/key_info_view";
import { migratedHref } from "@/utils/migratedPages";
import { ArrowLeftOutlined } from "@ant-design/icons";
import { Button } from "antd";
import { useRouter } from "next/navigation";
import { useState } from "react";
@ -22,19 +24,24 @@ export default function KeyDetailPage() {
const { data: keyData, isPending } = useKeyInfo(keyId);
const { data: teams } = useAllTeams();
const backToKeys = () => router.push(migratedHref("api-keys"));
if (authLoading || !isAuthorized) {
return <LoadingScreen />;
}
if (!keyId || isPending) {
return <LoadingScreen />;
}
if (!keyData) {
return (
<div className="p-4">
<Button type="text" icon={<ArrowLeftOutlined />} onClick={backToKeys} className="mb-4">
Back to Keys
</Button>
<p className="text-sm text-gray-700">Key not found</p>
</div>
);
}
return (
<KeyInfoView
keyId={keyId}
keyData={keyData ?? undefined}
teams={teams ?? []}
onClose={() => router.push(migratedHref("api-keys"))}
/>
);
return <KeyInfoView keyId={keyId} keyData={keyData} teams={teams ?? []} onClose={backToKeys} />;
}

View file

@ -50,13 +50,6 @@ const PREMIUM_METADATA_FIELDS = ["policies", "guardrails", "prompts", "tags", "a
const isEmptyValue = (v: unknown): boolean =>
v == null || (Array.isArray(v) && v.length === 0) || (typeof v === "string" && v.trim() === "");
/**
*
* @deprecated
* This component is being DEPRECATED in favor of src/app/(dashboard)/virtual-keys/components/KeyInfoView.tsx
* Please contribute to the new refactor.
*
*/
export default function KeyInfoView({
onClose,
keyData,