From c8b8dafd4091e87474fa36deba7d725f0a1e164b Mon Sep 17 00:00:00 2001 From: RissRIce Date: Thu, 30 Jul 2026 14:24:52 -0600 Subject: [PATCH] Validate GitHub repository IDs strictly --- .../[repoId]/actions/[actionId]/install/route.ts | 5 +++-- sites/sh1pt.com/lib/github-repo-id.test.ts | 15 +++++++++++++++ sites/sh1pt.com/lib/github-repo-id.ts | 6 ++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 sites/sh1pt.com/lib/github-repo-id.test.ts create mode 100644 sites/sh1pt.com/lib/github-repo-id.ts diff --git a/sites/sh1pt.com/app/api/v1/github/installations/[id]/repos/[repoId]/actions/[actionId]/install/route.ts b/sites/sh1pt.com/app/api/v1/github/installations/[id]/repos/[repoId]/actions/[actionId]/install/route.ts index 2495fdac..fafd64c7 100644 --- a/sites/sh1pt.com/app/api/v1/github/installations/[id]/repos/[repoId]/actions/[actionId]/install/route.ts +++ b/sites/sh1pt.com/app/api/v1/github/installations/[id]/repos/[repoId]/actions/[actionId]/install/route.ts @@ -7,6 +7,7 @@ import { } from '@profullstack/sh1pt-actions-fleet-core'; import { authorizeInstallation } from '@/lib/github-installation'; import { mintInstallationToken } from '@/lib/github-app'; +import { parseGithubRepoId } from '@/lib/github-repo-id'; import { getSupabaseServiceClient } from '@/lib/supabase/service'; export const runtime = 'nodejs'; @@ -34,8 +35,8 @@ export async function POST( const auth = await authorizeInstallation(id); if (auth instanceof NextResponse) return auth; - const githubRepoId = Number.parseInt(repoId, 10); - if (!Number.isFinite(githubRepoId)) { + const githubRepoId = parseGithubRepoId(repoId); + if (githubRepoId === null) { return NextResponse.json({ error: 'Invalid repo id' }, { status: 400 }); } diff --git a/sites/sh1pt.com/lib/github-repo-id.test.ts b/sites/sh1pt.com/lib/github-repo-id.test.ts new file mode 100644 index 00000000..040f0bd5 --- /dev/null +++ b/sites/sh1pt.com/lib/github-repo-id.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from 'vitest'; +import { parseGithubRepoId } from './github-repo-id'; + +describe('parseGithubRepoId', () => { + it('parses a positive safe integer', () => { + expect(parseGithubRepoId('123456789')).toBe(123456789); + }); + + it.each(['123abc', '123.5', '-123', '0', '', ' 123', '9007199254740992'])( + 'rejects invalid repo id %j', + (value) => { + expect(parseGithubRepoId(value)).toBeNull(); + }, + ); +}); diff --git a/sites/sh1pt.com/lib/github-repo-id.ts b/sites/sh1pt.com/lib/github-repo-id.ts new file mode 100644 index 00000000..0d3bedf7 --- /dev/null +++ b/sites/sh1pt.com/lib/github-repo-id.ts @@ -0,0 +1,6 @@ +export function parseGithubRepoId(value: string): number | null { + if (!/^[1-9]\d*$/.test(value)) return null; + + const repoId = Number(value); + return Number.isSafeInteger(repoId) ? repoId : null; +}