fix(api): Zod-validate POST /api/github-skills + document new gate envs + pin merge-integrity actions

Three latent heavy-CI reds surfaced by the VPS validation dispatch (the fast
path never runs these gates):

- t06 route-validation: POST /api/github-skills destructured request.json()
  blind — a non-array 'targets' would .map-crash. Now validateBody(zod)
  with defaults preserved (Hard Rule #7). Guard:
  tests/unit/github-skills-route-validation.test.ts (4/4).
- env-doc-sync: document OMNIROUTE_SKIP_SYSTEM_TRUST (#6310) and the
  changelog-integrity gate envs CHANGELOG_BASE_REF/ALLOW_CHANGELOG_REMOVALS
  (#6300) in .env.example + ENVIRONMENT.md.
- zizmor ratchet: the new merge-integrity job's checkout/setup-node uses were
  unpinned (+1 finding, 160 > baseline 159); pinned by SHA -> 158 (< baseline).
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-05 20:04:49 -03:00
parent 5c953d1f51
commit dd12539a2c
5 changed files with 64 additions and 14 deletions

View File

@@ -10,10 +10,18 @@
* Body: { repoName, targets, description }
*/
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { searchGitHubSkills } from "@/lib/skills/githubCollector";
import { matchesSearch } from "@/shared/utils/turkishText";
import { validateBody } from "@/shared/validation/helpers";
import { buildErrorBody, sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
const installSkillSchema = z.object({
repoName: z.string().min(1),
targets: z.array(z.string().min(1)).optional().default(["hermes"]),
description: z.string().optional().default(""),
});
export const dynamic = "force-dynamic";
export async function GET(request: NextRequest) {
@@ -57,20 +65,11 @@ export async function GET(request: NextRequest) {
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const {
repoName,
targets = ["hermes"],
description = "",
} = body as {
repoName?: string;
targets?: string[];
description?: string;
};
if (!repoName || typeof repoName !== "string") {
const parsed = validateBody(installSkillSchema, await request.json());
if (!parsed.success) {
return NextResponse.json(buildErrorBody(400, "repoName is required"), { status: 400 });
}
const { repoName, targets, description } = parsed.data;
const { resolveInstallPath, INSTALL_TARGETS } = await import("@/lib/skills/githubCollector");
const skillName = repoName.split("/").pop() || repoName;