fix(api): validate rolePresets with RolePresetsSchema before DB cast
Replace z.array(z.unknown()) with RolePresetsSchema for blueprint role presets mutation input, ensuring structural validation before Prisma JSON cast. Also adds SECURITY.md for vulnerability disclosure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you discover a security vulnerability in CapaKraken, please report it responsibly.
|
||||||
|
|
||||||
|
**Do not** open a public GitHub issue for security vulnerabilities.
|
||||||
|
|
||||||
|
Instead, please email the maintainer directly with:
|
||||||
|
|
||||||
|
1. A description of the vulnerability
|
||||||
|
2. Steps to reproduce
|
||||||
|
3. Potential impact assessment
|
||||||
|
|
||||||
|
We will acknowledge receipt within 48 hours and provide a timeline for resolution.
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Only the latest version on the `main` branch receives security updates.
|
||||||
|
|
||||||
|
## Security Practices
|
||||||
|
|
||||||
|
- Dependencies are audited nightly via `pnpm audit` and on every CI run
|
||||||
|
- Authentication uses Argon2-based password hashing via Auth.js v5
|
||||||
|
- Rate limiting is enforced on all API endpoints with Redis-backed counters
|
||||||
|
- All database mutations use parameterized queries via Prisma (no raw SQL)
|
||||||
|
- Session tokens are rotated on password change
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import { BlueprintTarget, CreateBlueprintSchema, UpdateBlueprintSchema } from "@capakraken/shared";
|
import {
|
||||||
|
BlueprintTarget,
|
||||||
|
CreateBlueprintSchema,
|
||||||
|
RolePresetsSchema,
|
||||||
|
UpdateBlueprintSchema,
|
||||||
|
} from "@capakraken/shared";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { findUniqueOrThrow } from "../db/helpers.js";
|
import { findUniqueOrThrow } from "../db/helpers.js";
|
||||||
import { makeAuditLogger } from "../lib/audit-helpers.js";
|
import { makeAuditLogger } from "../lib/audit-helpers.js";
|
||||||
@@ -54,7 +59,7 @@ export const blueprintUpdateInputSchema = z.object({
|
|||||||
|
|
||||||
export const blueprintRolePresetsInputSchema = z.object({
|
export const blueprintRolePresetsInputSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
rolePresets: z.array(z.unknown()).max(100),
|
rolePresets: RolePresetsSchema.max(100),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const blueprintBatchDeleteInputSchema = z.object({
|
export const blueprintBatchDeleteInputSchema = z.object({
|
||||||
|
|||||||
@@ -30,23 +30,23 @@ export async function findBlueprintByIdentifier<TBlueprint>(
|
|||||||
): Promise<TBlueprint> {
|
): Promise<TBlueprint> {
|
||||||
const normalizedIdentifier = identifier.trim();
|
const normalizedIdentifier = identifier.trim();
|
||||||
|
|
||||||
let blueprint = await db.blueprint.findUnique({
|
let blueprint = (await db.blueprint.findUnique({
|
||||||
where: { id: normalizedIdentifier },
|
where: { id: normalizedIdentifier },
|
||||||
...extraArgs,
|
...extraArgs,
|
||||||
}) as TBlueprint | null;
|
})) as TBlueprint | null;
|
||||||
|
|
||||||
if (!blueprint) {
|
if (!blueprint) {
|
||||||
blueprint = await db.blueprint.findFirst({
|
blueprint = (await db.blueprint.findFirst({
|
||||||
where: { name: { equals: normalizedIdentifier, mode: "insensitive" } },
|
where: { name: { equals: normalizedIdentifier, mode: "insensitive" } },
|
||||||
...extraArgs,
|
...extraArgs,
|
||||||
}) as TBlueprint | null;
|
})) as TBlueprint | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!blueprint) {
|
if (!blueprint) {
|
||||||
blueprint = await db.blueprint.findFirst({
|
blueprint = (await db.blueprint.findFirst({
|
||||||
where: { name: { contains: normalizedIdentifier, mode: "insensitive" } },
|
where: { name: { contains: normalizedIdentifier, mode: "insensitive" } },
|
||||||
...extraArgs,
|
...extraArgs,
|
||||||
}) as TBlueprint | null;
|
})) as TBlueprint | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!blueprint) {
|
if (!blueprint) {
|
||||||
@@ -91,7 +91,7 @@ export function buildBlueprintUpdateData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildBlueprintRolePresetsUpdateData(
|
export function buildBlueprintRolePresetsUpdateData(
|
||||||
rolePresets: unknown[],
|
rolePresets: readonly Record<string, unknown>[],
|
||||||
): Prisma.BlueprintUncheckedUpdateInput {
|
): Prisma.BlueprintUncheckedUpdateInput {
|
||||||
return {
|
return {
|
||||||
rolePresets: rolePresets as unknown as Prisma.InputJsonValue,
|
rolePresets: rolePresets as unknown as Prisma.InputJsonValue,
|
||||||
|
|||||||
Reference in New Issue
Block a user