mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 00:02:37 +00:00
fix(rpc): reject a blank GitHub owner or repo
normalizeTaskProviderIdentity treats a blank owner or repo as no identity at all, but the schema accepted '' and whitespace-only, so the two disagreed about the same payload. Refined rather than trimmed: trimming would rewrite the parsed value and change what the handler receives. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -108,3 +108,34 @@ describe('task provider identity RPC validation', () => {
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('github identity blank fields', () => {
|
||||
// The normalizer treats a blank owner or repo as no identity, so the schema must agree.
|
||||
it.each(['', ' ', '\t'])('rejects a blank owner %j', (owner) => {
|
||||
expect(
|
||||
TaskProviderIdentity.safeParse({ provider: 'github', owner, repo: 'orca' }).success
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it.each(['', ' '])('rejects a blank repo %j', (repo) => {
|
||||
expect(
|
||||
TaskProviderIdentity.safeParse({ provider: 'github', owner: 'stablyai', repo }).success
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('still accepts a populated identity', () => {
|
||||
expect(
|
||||
TaskProviderIdentity.safeParse({ provider: 'github', owner: 'stablyai', repo: 'orca' })
|
||||
.success
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('leaves the parsed value untrimmed, so no wire bytes change', () => {
|
||||
const parsed = TaskProviderIdentity.safeParse({
|
||||
provider: 'github',
|
||||
owner: ' stablyai ',
|
||||
repo: 'orca'
|
||||
})
|
||||
expect(parsed.success && parsed.data?.owner).toBe(' stablyai ')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,13 +57,21 @@ export const OptionalNullablePlainString = z
|
||||
.pipe(z.union([z.string(), z.null(), z.undefined()]))
|
||||
.optional()
|
||||
|
||||
// A GitHub identity is only usable with both fields present and non-blank.
|
||||
const GithubIdentityField = z.string().refine((value) => value.trim().length > 0, {
|
||||
message: 'Required'
|
||||
})
|
||||
|
||||
export const TaskProviderIdentity = z
|
||||
.discriminatedUnion('provider', [
|
||||
z
|
||||
.object({
|
||||
provider: z.literal('github'),
|
||||
owner: z.string(),
|
||||
repo: z.string(),
|
||||
// Why refine, not .trim(): normalizeTaskProviderIdentity treats a blank owner or repo as
|
||||
// no identity at all, so blank must be rejected here — but trimming would rewrite the
|
||||
// parsed value and change what the handler receives.
|
||||
owner: GithubIdentityField,
|
||||
repo: GithubIdentityField,
|
||||
host: z.string().optional()
|
||||
})
|
||||
.passthrough(),
|
||||
|
||||
Reference in New Issue
Block a user