mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 02:11:28 +00:00
fix(channels): reject oversized official channel tokens
Add a 4096-character cap before persisting official channel tokens, map that validation failure to HTTP 400 in the dashboard route, and cover both paths with regression tests.
This commit is contained in:
@@ -22,6 +22,8 @@ export interface OfficialChannelTokenStatus {
|
||||
processEnvAvailable: boolean;
|
||||
}
|
||||
|
||||
const MAX_OFFICIAL_CHANNEL_TOKEN_LENGTH = 4096;
|
||||
|
||||
function getResolvedStateDirOverride(
|
||||
channelId: OfficialChannelId,
|
||||
envOverrides?: NodeJS.ProcessEnv | null
|
||||
@@ -293,6 +295,11 @@ export function setConfiguredOfficialChannelToken(
|
||||
if (!normalized) {
|
||||
throw new Error(`${envKey} cannot be empty or multiline.`);
|
||||
}
|
||||
if (normalized.length > MAX_OFFICIAL_CHANNEL_TOKEN_LENGTH) {
|
||||
throw new Error(
|
||||
`${envKey} cannot exceed ${MAX_OFFICIAL_CHANNEL_TOKEN_LENGTH} characters.`
|
||||
);
|
||||
}
|
||||
|
||||
const envPath = getOfficialChannelEnvPath(channelId);
|
||||
const currentContent = readFileIfExists(envPath) ?? '';
|
||||
|
||||
@@ -178,7 +178,8 @@ router.put('/:channelId/token', (req: Request, res: Response): void => {
|
||||
res.json({ success: true, tokenConfigured: true, tokenPath });
|
||||
} catch (error) {
|
||||
const message = (error as Error).message;
|
||||
const statusCode = message.includes('cannot be empty') ? 400 : 500;
|
||||
const statusCode =
|
||||
message.includes('cannot be empty') || message.includes('cannot exceed') ? 400 : 500;
|
||||
res.status(statusCode).json({ error: message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -56,6 +56,16 @@ describe('official channels token store', () => {
|
||||
expect(readConfiguredOfficialChannelToken('telegram')).toBe('telegram-secret');
|
||||
});
|
||||
|
||||
it('rejects oversized official channel tokens before writing channel state', () => {
|
||||
const envPath = getOfficialChannelEnvPath('discord');
|
||||
|
||||
expect(() => setConfiguredOfficialChannelToken('discord', 'x'.repeat(4097))).toThrow(
|
||||
'DISCORD_BOT_TOKEN cannot exceed 4096 characters.'
|
||||
);
|
||||
expect(fs.existsSync(envPath)).toBe(false);
|
||||
expect(hasConfiguredOfficialChannelToken('discord')).toBe(false);
|
||||
});
|
||||
|
||||
it('uses the official state-dir override when one is configured', () => {
|
||||
const originalDiscordStateDir = process.env.DISCORD_STATE_DIR;
|
||||
process.env.DISCORD_STATE_DIR = path.join(tempHome, 'discord-state');
|
||||
|
||||
@@ -194,4 +194,16 @@ describe('web-server channels-routes', () => {
|
||||
else delete process.env.DISCORD_BOT_TOKEN;
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects oversized token writes with a 400 response', async () => {
|
||||
const response = await putJson(baseUrl, '/api/channels/discord/token', {
|
||||
token: 'x'.repeat(4097),
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(await response.json()).toEqual({
|
||||
error: 'DISCORD_BOT_TOKEN cannot exceed 4096 characters.',
|
||||
});
|
||||
expect(fs.existsSync(path.join(tempHome, '.claude', 'channels', 'discord', '.env'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user