mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(qa): apply pre-release multi-agent QA findings
Three small follow-ups surfaced by the v1.4.6 QA pass:
- Settings → KI: the "raw data" warning still used raw Tailwind orange
tokens instead of the Dracula `dracula-orange` ones, breaking theme
consistency and tipping below the WCAG-AA contrast floor on the
card surface.
- Admin status grid: the Backups card cta read "View backups" but
jumped to the system-status section (the dedicated backups view
does not exist yet — that's a v1.5 feature). Re-labelling the cta
to "Open system status" and the Integrations cta to "Open
integrations" makes the destination honest. The href targets
themselves are unchanged.
- `withIdempotency` body-content guard previously did `text.includes("sk-")`
which silently broke caching for benign payloads containing common
English compounds like "task-id", "risk-management", "disk-io".
Tightened to a regex requiring the canonical secret prefix plus a
realistic suffix length, so only actual API keys trip the guard.
Three regression tests cover the false-positive cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
dc4507a55f
commit
67575180b8
4 files changed
+28
-9
No files matched your search
@@ -157,7 +157,7 @@ export function buildCards(data: StatusOverview): StatusCardProps[] {
|
||||
{ label: "Push", value: data.integrations.webPush },
|
||||
],
|
||||
href: "/admin#section-admin-umami",
|
||||
cta: "Configure",
|
||||
cta: "Open integrations",
|
||||
},
|
||||
{
|
||||
title: "Monitoring",
|
||||
@@ -187,7 +187,7 @@ export function buildCards(data: StatusOverview): StatusCardProps[] {
|
||||
{ label: "Retain", value: `${data.backups.retentionDays}d` },
|
||||
],
|
||||
href: "/admin#section-system-status",
|
||||
cta: "View backups",
|
||||
cta: "Open system status",
|
||||
},
|
||||
{
|
||||
title: "Maintenance",
|
||||
|
||||
@@ -362,7 +362,7 @@ function InsightsSettingsCard({
|
||||
</div>
|
||||
</div>
|
||||
{settings?.privacyMode === "raw" && (
|
||||
<div className="mt-2 rounded-lg bg-orange-500/10 p-2 text-xs text-orange-400">
|
||||
<div className="bg-dracula-orange/15 text-dracula-orange mt-2 rounded-lg p-2 text-xs">
|
||||
{t("settings.rawDataWarning")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -300,6 +300,24 @@ describe("withIdempotency body-content exclusion (P12)", () => {
|
||||
expect(prisma.idempotencyKey.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["task-id substring", '{"error":"task-id must not contain spaces"}'],
|
||||
["risk-management word", '{"data":{"note":"risk-management"}}'],
|
||||
["disk-io metric", '{"data":{"metric":"disk-io"}}'],
|
||||
])(
|
||||
"still caches a benign payload whose text contains %s (no false positive)",
|
||||
async (_label, body) => {
|
||||
const handler = vi.fn(
|
||||
async () => new NextResponse(body, { status: 422 }),
|
||||
);
|
||||
const wrapped = withIdempotency<[NextRequest]>(handler, async () => "u-1");
|
||||
await wrapped(
|
||||
makeRequest("POST", { "idempotency-key": "key-12345678" }),
|
||||
);
|
||||
expect(prisma.idempotencyKey.create).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
);
|
||||
|
||||
it("still caches a normal payload that does not carry a secret", async () => {
|
||||
const handler = vi.fn(
|
||||
async () =>
|
||||
|
||||
@@ -224,14 +224,15 @@ export function withIdempotency<
|
||||
// with, but if a future caller forgets we refuse to leak.
|
||||
// `hlk_` = our access tokens
|
||||
// `hlr_` = our refresh tokens
|
||||
// `sk-` = OpenAI keys (also matches `sk-ant-` Anthropic keys)
|
||||
// `sk-…` = OpenAI / Anthropic keys (full token form, not the
|
||||
// raw substring — a 422 body explaining "task-id…"
|
||||
// or any other word containing `sk-` would otherwise
|
||||
// silently break idempotency for benign retries).
|
||||
const SECRET_PATTERN =
|
||||
/(?:\b(?:hlk_|hlr_)[A-Za-z0-9_-]+|\bsk-(?:ant-)?[A-Za-z0-9_-]{8,})/;
|
||||
const cloned = response.clone();
|
||||
const text = await cloned.text();
|
||||
if (
|
||||
!text.includes("hlk_") &&
|
||||
!text.includes("hlr_") &&
|
||||
!text.includes("sk-")
|
||||
) {
|
||||
if (!SECRET_PATTERN.test(text)) {
|
||||
await persistCached(ctx, response, text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user