kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add blacklist for namespaces (usernames + team names)
rodzic
0f6fcff943
commit
fbb9863208
|
@ -1,5 +1,6 @@
|
|||
import { assert } from '@agentic/platform-core'
|
||||
import {
|
||||
isNamespaceAllowed,
|
||||
isValidCuid,
|
||||
isValidDeploymentIdentifier,
|
||||
isValidProjectIdentifier,
|
||||
|
@ -82,12 +83,20 @@ export const usernameSchema = z
|
|||
.refine((username) => isValidUsername(username), {
|
||||
message: 'Invalid username'
|
||||
})
|
||||
.refine((username) => isNamespaceAllowed(username), {
|
||||
message:
|
||||
'Username is not allowed (reserved, offensive, or otherwise confusing)'
|
||||
})
|
||||
|
||||
export const teamSlugSchema = z
|
||||
.string()
|
||||
.refine((slug) => isValidTeamSlug(slug), {
|
||||
message: 'Invalid team slug'
|
||||
})
|
||||
.refine((slug) => isNamespaceAllowed(slug), {
|
||||
message:
|
||||
'Team slug is not allowed (reserved, offensive, or otherwise confusing)'
|
||||
})
|
||||
|
||||
export const paginationSchema = z.object({
|
||||
offset: z.number().int().nonnegative().default(0).optional(),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from './namespace-blacklist'
|
||||
export * from './parse-deployment-identifier'
|
||||
export * from './parse-project-identifier'
|
||||
export * from './parse-tool-identifier'
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
export const namespaceBlacklist = new Set([
|
||||
// restricted because they would be confusing
|
||||
'agentic',
|
||||
'admin',
|
||||
'root',
|
||||
'sudo',
|
||||
'mcp',
|
||||
'sse',
|
||||
'raw',
|
||||
'todo',
|
||||
'team',
|
||||
'api',
|
||||
'user',
|
||||
'ai',
|
||||
'llm',
|
||||
'agent',
|
||||
'agents',
|
||||
'free',
|
||||
'paid',
|
||||
'tool',
|
||||
'tools',
|
||||
'not',
|
||||
'openapi',
|
||||
'error',
|
||||
'legal',
|
||||
'legacy',
|
||||
'support',
|
||||
'email',
|
||||
'help',
|
||||
'faq',
|
||||
'privacy',
|
||||
'terms',
|
||||
'about',
|
||||
'contact',
|
||||
'internal',
|
||||
'public',
|
||||
'private',
|
||||
'404',
|
||||
'500',
|
||||
'403',
|
||||
'401',
|
||||
'400',
|
||||
'409',
|
||||
'408',
|
||||
'407',
|
||||
'406',
|
||||
|
||||
// bad words
|
||||
'fuck',
|
||||
'shit',
|
||||
'bitch',
|
||||
'cunt',
|
||||
'dick',
|
||||
'pussy',
|
||||
'ass',
|
||||
'asshole',
|
||||
'faggot',
|
||||
'fag',
|
||||
'fagot',
|
||||
'nigger',
|
||||
'nigga',
|
||||
'niggers',
|
||||
'niggas',
|
||||
'niggers',
|
||||
'porn',
|
||||
'sex',
|
||||
'anal',
|
||||
'blowjob',
|
||||
'cum',
|
||||
'penis',
|
||||
'vagina',
|
||||
'boobs',
|
||||
'tits',
|
||||
|
||||
// reserved for companies
|
||||
'openai',
|
||||
'anthropic',
|
||||
'google',
|
||||
'microsoft',
|
||||
'meta',
|
||||
'alibaba',
|
||||
'amazon',
|
||||
'apple',
|
||||
'arista',
|
||||
'atlassian',
|
||||
'aws',
|
||||
'azure',
|
||||
'cloudflare',
|
||||
'cisco',
|
||||
'stripe',
|
||||
'vercel',
|
||||
'facebook',
|
||||
'twitter',
|
||||
'x',
|
||||
'instagram',
|
||||
'tiktok',
|
||||
'youtube',
|
||||
'linkedin',
|
||||
'github',
|
||||
'gitlab',
|
||||
'bitbucket',
|
||||
'slack',
|
||||
'discord',
|
||||
'telegram',
|
||||
'whatsapp',
|
||||
'snapchat',
|
||||
'pinterest',
|
||||
'reddit',
|
||||
'tumblr',
|
||||
'pornhub',
|
||||
'xvideos',
|
||||
|
||||
// reserved for individuals
|
||||
'trump',
|
||||
'elon',
|
||||
'elonmusk'
|
||||
])
|
|
@ -1,6 +1,7 @@
|
|||
import { expect, test } from 'vitest'
|
||||
|
||||
import {
|
||||
isNamespaceAllowed,
|
||||
isValidDeploymentHash,
|
||||
isValidDeploymentIdentifier,
|
||||
isValidEmail,
|
||||
|
@ -11,19 +12,28 @@ import {
|
|||
isValidUsername
|
||||
} from './validators'
|
||||
|
||||
test('email success', () => {
|
||||
test('isValidEmail success', () => {
|
||||
expect(isValidEmail('t@t.com')).toBe(true)
|
||||
expect(isValidEmail('abc@gmail.com')).toBe(true)
|
||||
expect(isValidEmail('abc@foo.io')).toBe(true)
|
||||
})
|
||||
|
||||
test('email failure', () => {
|
||||
test('isValidEmail failure', () => {
|
||||
expect(isValidEmail('t@t')).toBe(false)
|
||||
expect(isValidEmail('abc')).toBe(false)
|
||||
expect(isValidEmail('@')).toBe(false)
|
||||
})
|
||||
|
||||
test('username success', () => {
|
||||
test('isNamespaceAllowed', () => {
|
||||
expect(isNamespaceAllowed('foo')).toBe(true)
|
||||
expect(isNamespaceAllowed('foo-bar')).toBe(true)
|
||||
expect(isNamespaceAllowed('vercel')).toBe(false)
|
||||
expect(isNamespaceAllowed('ai')).toBe(false)
|
||||
expect(isNamespaceAllowed('fuck')).toBe(false)
|
||||
expect(isNamespaceAllowed('agentic')).toBe(false)
|
||||
})
|
||||
|
||||
test('isValidUsername success', () => {
|
||||
expect(isValidUsername('z')).toBe(true)
|
||||
expect(isValidUsername('abc')).toBe(true)
|
||||
expect(isValidUsername('abc-123')).toBe(true)
|
||||
|
@ -32,7 +42,7 @@ test('username success', () => {
|
|||
expect(isValidUsername('a'.repeat(256))).toBe(true)
|
||||
})
|
||||
|
||||
test('username failure (invalid)', () => {
|
||||
test('isValidUsername failure (invalid)', () => {
|
||||
expect(isValidUsername('ab%')).toBe(false)
|
||||
expect(isValidUsername('.')).toBe(false)
|
||||
expect(isValidUsername('$')).toBe(false)
|
||||
|
@ -41,19 +51,19 @@ test('username failure (invalid)', () => {
|
|||
expect(isValidUsername('a'.repeat(257))).toBe(false)
|
||||
})
|
||||
|
||||
test('password success', () => {
|
||||
test('isValidPassword success', () => {
|
||||
expect(isValidPassword('abc')).toBe(true)
|
||||
expect(isValidPassword('password')).toBe(true)
|
||||
expect(isValidPassword('asldfkjasldkfjlaksdfjlkas')).toBe(true)
|
||||
})
|
||||
|
||||
test('password failure', () => {
|
||||
test('isValidPassword failure', () => {
|
||||
expect(isValidPassword('aa')).toBe(false)
|
||||
expect(isValidPassword('.'))
|
||||
expect(isValidPassword('a'.repeat(1025))).toBe(false)
|
||||
})
|
||||
|
||||
test('projectName success', () => {
|
||||
test('isValidProjectName success', () => {
|
||||
expect(isValidProjectName('a')).toBe(true)
|
||||
expect(isValidProjectName('ai')).toBe(true)
|
||||
expect(isValidProjectName('aaa')).toBe(true)
|
||||
|
@ -62,7 +72,7 @@ test('projectName success', () => {
|
|||
expect(isValidProjectName('f'.repeat(256))).toBe(true)
|
||||
})
|
||||
|
||||
test('projectName failure', () => {
|
||||
test('isValidProjectName failure', () => {
|
||||
expect(isValidProjectName('hello_world')).toBe(false)
|
||||
expect(isValidProjectName('a_bc')).toBe(false)
|
||||
expect(isValidProjectName('abc.')).toBe(false)
|
||||
|
@ -72,25 +82,25 @@ test('projectName failure', () => {
|
|||
expect(isValidProjectName('f'.repeat(257))).toBe(false)
|
||||
})
|
||||
|
||||
test('deploymentHash success', () => {
|
||||
test('isValidDeploymentHash success', () => {
|
||||
expect(isValidDeploymentHash('abcdefgh')).toBe(true)
|
||||
expect(isValidDeploymentHash('01234567')).toBe(true)
|
||||
expect(isValidDeploymentHash('k2l3n6l2')).toBe(true)
|
||||
})
|
||||
|
||||
test('deploymentHash failure', () => {
|
||||
test('isValidDeploymentHash failure', () => {
|
||||
expect(isValidDeploymentHash('aa')).toBe(false)
|
||||
expect(isValidDeploymentHash('')).toBe(false)
|
||||
expect(isValidDeploymentHash('Abcdefgh')).toBe(false)
|
||||
expect(isValidDeploymentHash('012345678')).toBe(false)
|
||||
})
|
||||
|
||||
test('projectIdentifier success', () => {
|
||||
test('isValidProjectIdentifier success', () => {
|
||||
expect(isValidProjectIdentifier('@username/project-name')).toBe(true)
|
||||
expect(isValidProjectIdentifier('@a/123')).toBe(true)
|
||||
})
|
||||
|
||||
test('projectIdentifier failure', () => {
|
||||
test('isValidProjectIdentifier failure', () => {
|
||||
expect(isValidProjectIdentifier('')).toBe(false)
|
||||
expect(isValidProjectIdentifier()).toBe(false)
|
||||
expect(isValidProjectIdentifier('foo')).toBe(false)
|
||||
|
@ -108,7 +118,7 @@ test('projectIdentifier failure', () => {
|
|||
expect(isValidProjectIdentifier('@_/___')).toBe(false)
|
||||
})
|
||||
|
||||
test('deploymentIdentifier success', () => {
|
||||
test('isValidDeploymentIdentifier success', () => {
|
||||
expect(isValidDeploymentIdentifier('@username/project-name@01234567')).toBe(
|
||||
true
|
||||
)
|
||||
|
@ -125,7 +135,7 @@ test('deploymentIdentifier success', () => {
|
|||
expect(isValidDeploymentIdentifier('@a/123')).toBe(true)
|
||||
})
|
||||
|
||||
test('deploymentIdentifier failure', () => {
|
||||
test('isValidDeploymentIdentifier failure', () => {
|
||||
expect(isValidDeploymentIdentifier('')).toBe(false)
|
||||
expect(isValidDeploymentIdentifier()).toBe(false)
|
||||
expect(isValidDeploymentIdentifier('foo')).toBe(false)
|
||||
|
@ -144,7 +154,7 @@ test('deploymentIdentifier failure', () => {
|
|||
expect(isValidDeploymentIdentifier('012345678/123@1.0.1')).toBe(false)
|
||||
})
|
||||
|
||||
test('toolName success', () => {
|
||||
test('isValidToolName success', () => {
|
||||
expect(isValidToolName('tool_name')).toBe(true)
|
||||
expect(isValidToolName('toolName')).toBe(true)
|
||||
expect(isValidToolName('_identIFIER0123')).toBe(true)
|
||||
|
@ -155,7 +165,7 @@ test('toolName success', () => {
|
|||
expect(isValidToolName('_searchGoogle')).toBe(true)
|
||||
})
|
||||
|
||||
test('toolName failure', () => {
|
||||
test('isValidToolName failure', () => {
|
||||
expect(isValidToolName('ab1.2')).toBe(false)
|
||||
expect(isValidToolName('foo-bar')).toBe(false)
|
||||
expect(isValidToolName('abc/123')).toBe(false)
|
||||
|
|
|
@ -2,6 +2,7 @@ import { isCuid } from '@paralleldrive/cuid2'
|
|||
import emailValidator from 'email-validator'
|
||||
|
||||
import type { ParseIdentifierOptions } from './types'
|
||||
import { namespaceBlacklist } from './namespace-blacklist'
|
||||
import { parseDeploymentIdentifier } from './parse-deployment-identifier'
|
||||
import { parseProjectIdentifier } from './parse-project-identifier'
|
||||
|
||||
|
@ -21,6 +22,10 @@ export function isValidNamespace(value?: string): boolean {
|
|||
return !!value && namespaceRe.test(value)
|
||||
}
|
||||
|
||||
export function isNamespaceAllowed(value?: string): boolean {
|
||||
return !!value && isValidNamespace(value) && !namespaceBlacklist.has(value)
|
||||
}
|
||||
|
||||
export function isValidUsername(value?: string): boolean {
|
||||
return isValidNamespace(value)
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
- raw
|
||||
- auth
|
||||
- custom auth pages for `openauth`
|
||||
- add username / team name blacklist
|
||||
- admin, internal, mcp, sse, etc
|
||||
- API gateway
|
||||
- how to handle binary bodies and responses?
|
||||
- add support for `immutable` in `toolConfigs`
|
||||
|
|
Ładowanie…
Reference in New Issue