fix(cliproxy): sanitize Codex quota labels

This commit is contained in:
Kai (Tam Nhu) Tran
2026-05-30 14:53:53 -04:00
parent c21f589247
commit 457412277b
5 changed files with 105 additions and 10 deletions
@@ -382,6 +382,44 @@ describe('Codex Quota Fetcher', () => {
expect(windows[0].featureLabel).toBe('Custom-Feature');
expect(windows[0].usedPercent).toBe(50);
});
it('should remove terminal control characters from additional limit labels', () => {
const response = {
additional_rate_limits: [
{
limit_name: '\u001b[2JGPT-5.3-Codex-Spark\u001b]52;c;payload\u0007',
rate_limit: {
primary_window: { used_percent: 25, reset_after_seconds: 3600 },
},
},
],
};
const windows = buildCodexQuotaWindows(response);
expect(windows).toHaveLength(1);
expect(windows[0].featureLabel).toBe('GPT-5.3-Codex-Spark');
expect(windows[0].label).toBe('GPT-5.3-Codex-Spark (Primary)');
});
it('should bound additional limit labels before storing them', () => {
const response = {
additional_rate_limits: [
{
limit_name: `Feature-${'x'.repeat(120)}`,
rate_limit: {
primary_window: { used_percent: 25, reset_after_seconds: 3600 },
},
},
],
};
const windows = buildCodexQuotaWindows(response);
expect(windows).toHaveLength(1);
expect(windows[0].featureLabel).toHaveLength(80);
expect(windows[0].label).toBe(`${windows[0].featureLabel} (Primary)`);
});
});
describe('buildCodexCoreUsageSummary', () => {
+4 -7
View File
@@ -11,6 +11,7 @@ import { getAuthDir } from '../config/config-generator';
import { getAccount, getProviderAccounts, getPausedDir } from '../accounts/account-manager';
import { sanitizeEmail, isTokenExpired } from '../auth/auth-utils';
import type { CodexQuotaResult, CodexQuotaWindow, CodexCoreUsageSummary } from './quota-types';
import { sanitizeCodexFeatureLabel } from './quota-label-sanitizer';
import { extractCanonicalEmailFromAccountId } from '../accounts/email-account-identity';
/** ChatGPT backend API base URL */
@@ -58,8 +59,8 @@ interface CodexRateLimitWindow {
* Each entry surfaces its own primary/secondary windows under a feature-specific limit name.
*/
interface CodexAdditionalRateLimit {
limit_name?: string;
limitName?: string;
limit_name?: unknown;
limitName?: unknown;
metered_feature?: string;
meteredFeature?: string;
rate_limit?: CodexRateLimitWindow;
@@ -380,11 +381,7 @@ function buildCodexQuotaWindows(payload: CodexUsageResponse): CodexQuotaWindow[]
const entryRateLimit = entry.rate_limit || entry.rateLimit;
if (!entryRateLimit) continue;
const rawFeatureLabel = entry.limit_name ?? entry.limitName;
const featureLabel =
typeof rawFeatureLabel === 'string' && rawFeatureLabel.trim().length > 0
? rawFeatureLabel.trim()
: 'Additional';
const featureLabel = sanitizeCodexFeatureLabel(entry.limit_name ?? entry.limitName);
addWindow(
`${featureLabel} (Primary)`,
entryRateLimit.primary_window || entryRateLimit.primaryWindow,
@@ -0,0 +1,25 @@
const CODEX_FEATURE_LABEL_FALLBACK = 'Additional';
const CODEX_FEATURE_LABEL_MAX_LENGTH = 80;
const TERMINAL_ESCAPE_SEQUENCE_REGEX =
/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?|\u001b\[[0-?]*[ -/]*[@-~]/g;
const TERMINAL_CONTROL_CHARS_REGEX = /[\u0000-\u001f\u007f-\u009f]/g;
/**
* Sanitize upstream Codex feature labels before storing or rendering them.
*
* The quota API is not schema-validated at runtime, so additional-rate-limit
* labels must be constrained to safe, printable strings before they reach the
* terminal.
*/
export function sanitizeCodexFeatureLabel(value: unknown): string {
if (typeof value !== 'string') return CODEX_FEATURE_LABEL_FALLBACK;
const sanitized = value
.replace(TERMINAL_ESCAPE_SEQUENCE_REGEX, '')
.replace(TERMINAL_CONTROL_CHARS_REGEX, '')
.trim()
.slice(0, CODEX_FEATURE_LABEL_MAX_LENGTH)
.trimEnd();
return sanitized.length > 0 ? sanitized : CODEX_FEATURE_LABEL_FALLBACK;
}
+6 -3
View File
@@ -18,6 +18,7 @@ import {
} from '../../cliproxy/accounts/account-manager';
import { fetchAllProviderQuotas } from '../../cliproxy/quota/quota-fetcher';
import { fetchAllCodexQuotas } from '../../cliproxy/quota/quota-fetcher-codex';
import { sanitizeCodexFeatureLabel } from '../../cliproxy/quota/quota-label-sanitizer';
import { fetchAllClaudeQuotas } from '../../cliproxy/quota/quota-fetcher-claude';
import { pickMostRestrictiveClaudeWeeklyWindow } from '../../cliproxy/quota/quota-fetcher-claude-normalizer';
import { fetchAllGeminiCliQuotas } from '../../cliproxy/quota/quota-fetcher-gemini-cli';
@@ -279,8 +280,8 @@ function inferCodeReviewCadence(
* Strip a leading "GPT-X.Y-Codex-" prefix from a feature label and turn the
* remainder into a Codex-prefixed display name. Other labels pass through unchanged.
*/
function prettifyCodexFeatureLabel(featureLabel: string): string {
const trimmed = featureLabel.trim();
function prettifyCodexFeatureLabel(featureLabel: unknown): string {
const trimmed = sanitizeCodexFeatureLabel(featureLabel);
if (!trimmed) return 'Additional';
const stripped = trimmed.replace(/^GPT-[\d.]+-Codex-/i, '');
if (stripped !== trimmed && stripped.length > 0) {
@@ -302,7 +303,7 @@ function getCodexWindowDisplayLabel(
}
if (window.category === 'additional') {
const pretty = prettifyCodexFeatureLabel(window.featureLabel || window.label || 'Additional');
const pretty = prettifyCodexFeatureLabel(window.featureLabel ?? window.label);
if (window.cadence === '5h') return `${pretty} (5h)`;
if (window.cadence === 'weekly') return `${pretty} (weekly)`;
return pretty;
@@ -850,7 +851,9 @@ const QUOTA_PROVIDER_RUNTIME: Record<QuotaSupportedProvider, QuotaProviderRuntim
};
export const __testExports = {
getCodexWindowDisplayLabel,
getQuotaFailureDisplayEntries,
prettifyCodexFeatureLabel,
resolveDisplayedTier,
};
@@ -84,3 +84,35 @@ describe('cliproxy quota subcommand failure formatting', () => {
expect(resolveDisplayedTier('pro', 'unknown')).toBe('pro');
});
});
describe('cliproxy quota subcommand Codex label formatting', () => {
it('falls back for non-string cached Codex feature labels', async () => {
const { getCodexWindowDisplayLabel } = await loadQuotaCommandTestExports();
const label = getCodexWindowDisplayLabel({
label: 'ignored',
resetAfterSeconds: 3600,
category: 'additional',
cadence: '5h',
featureLabel: { unexpected: true },
} as never);
expect(label).toBe('Additional (5h)');
});
it('removes terminal control characters from cached Codex feature labels', async () => {
const { getCodexWindowDisplayLabel } = await loadQuotaCommandTestExports();
const label = getCodexWindowDisplayLabel({
label: 'ignored',
resetAfterSeconds: 3600,
category: 'additional',
cadence: 'weekly',
featureLabel: '\u001b[2JGPT-5.3-Codex-Spark\u001b]52;c;payload\u0007',
});
expect(label).toBe('Codex Spark (weekly)');
expect(label).not.toContain('\u001b');
expect(label).not.toContain('\u0007');
});
});