Skip to content

Commit 857a668

Browse files
committed
fix(cli): fail gracefully on invalid scope:team format
The `/team/:scopeTeam/users` endpoint was letting validation errors from `validateScopeTeam` bubble up as an unhandled 500 response with an unhandled rejection logged to the console. Now it catches and returns a proper 400 with a helpful message, and logs the full error.
1 parent 2df63c6 commit 857a668

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

cli/src/server.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { H3, HTTPError, handleCors, type H3Event } from 'h3-next'
33
import type { CorsOptions } from 'h3-next'
44

55
import type { ConnectorState, PendingOperation, OperationType, ApiResponse } from './types.ts'
6+
import { logWarning } from './logger.ts'
67
import {
78
getNpmUser,
89
orgAddUser,
@@ -20,6 +21,7 @@ import {
2021
ownerAdd,
2122
ownerRemove,
2223
packageInit,
24+
validateScopeTeam,
2325
type NpmExecResult,
2426
} from './npm-client.ts'
2527

@@ -447,6 +449,18 @@ export function createConnectorApp(expectedToken: string) {
447449
// Decode the team name (handles encoded colons like nuxt%3Adevelopers)
448450
const scopeTeam = decodeURIComponent(scopeTeamRaw)
449451

452+
try {
453+
validateScopeTeam(scopeTeam)
454+
} catch (err) {
455+
logWarning(
456+
`Invalid scope:team format: ${scopeTeam} - ${err instanceof Error ? err.message : err}`,
457+
)
458+
throw new HTTPError({
459+
statusCode: 400,
460+
message: `Invalid scope:team format: ${scopeTeam}. Expected @scope:team`,
461+
})
462+
}
463+
450464
const result = await teamListUsers(scopeTeam)
451465
if (result.exitCode !== 0) {
452466
return {

test/unit/cli-server.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { createConnectorApp } from '../../cli/src/server.ts'
3+
4+
const TEST_TOKEN = 'test-token-123'
5+
6+
describe('connector server', () => {
7+
describe('GET /team/:scopeTeam/users', () => {
8+
it('returns 400 for invalid scope:team format (missing @ prefix)', async () => {
9+
const app = createConnectorApp(TEST_TOKEN)
10+
11+
const response = await app.fetch(
12+
new Request('http://localhost/team/netlify%3Adevelopers/users', {
13+
headers: { Authorization: `Bearer ${TEST_TOKEN}` },
14+
}),
15+
)
16+
17+
expect(response.status).toBe(400)
18+
const body = await response.json()
19+
expect(body.message).toContain('Invalid scope:team format')
20+
})
21+
22+
it('returns 401 without auth token', async () => {
23+
const app = createConnectorApp(TEST_TOKEN)
24+
25+
const response = await app.fetch(
26+
new Request('http://localhost/team/@netlify%3Adevelopers/users'),
27+
)
28+
29+
expect(response.status).toBe(401)
30+
})
31+
})
32+
})

0 commit comments

Comments
 (0)