fix(safety): unify add-account typed risk phrase

- change required acknowledgement phrase to "I ACCEPT RISK"

- apply typed phrase requirement to Gemini and Antigravity add-account flows

- keep CLI/server and UI phrase validation aligned

- update antigravity responsibility unit test
This commit is contained in:
Tam Nhu Tran
2026-02-24 18:32:07 +07:00
parent e22d331bf1
commit c0eb786127
5 changed files with 44 additions and 30 deletions
+2 -1
View File
@@ -14,7 +14,8 @@ import { getCliproxySafetyConfig } from '../config/unified-config-loader';
export const ANTIGRAVITY_RISK_ISSUE_URL = 'https://github.com/kaitranntt/ccs/issues/509'; export const ANTIGRAVITY_RISK_ISSUE_URL = 'https://github.com/kaitranntt/ccs/issues/509';
export const ANTIGRAVITY_ACK_VERSION = '2026-02-24-antigravity-oauth-v2'; export const ANTIGRAVITY_ACK_VERSION = '2026-02-24-antigravity-oauth-v2';
export const ANTIGRAVITY_ACK_PHRASE = 'I ACCEPT AGY RISK'; export const RISK_ACK_PHRASE = 'I ACCEPT RISK';
export const ANTIGRAVITY_ACK_PHRASE = RISK_ACK_PHRASE;
export const ANTIGRAVITY_ACCEPT_RISK_FLAGS = ['--accept-agr-risk', '--accept-antigravity-risk']; export const ANTIGRAVITY_ACCEPT_RISK_FLAGS = ['--accept-agr-risk', '--accept-antigravity-risk'];
type AgyRiskContext = 'oauth' | 'run'; type AgyRiskContext = 'oauth' | 'run';
@@ -57,7 +57,7 @@ describe('antigravity-responsibility', () => {
reviewedIssue509: true, reviewedIssue509: true,
understandsBanRisk: true, understandsBanRisk: true,
acceptsFullResponsibility: true, acceptsFullResponsibility: true,
typedPhrase: ' i accept agy risk ', typedPhrase: ' i accept risk ',
}); });
expect(result.valid).toBeTrue(); expect(result.valid).toBeTrue();
@@ -1,14 +1,16 @@
import { AlertTriangle, ExternalLink, Settings2 } from 'lucide-react'; import { AlertTriangle, ExternalLink, Settings2 } from 'lucide-react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { RISK_ACK_PHRASE } from '@/components/account/antigravity-responsibility-constants';
interface AccountSafetyWarningCardProps { interface AccountSafetyWarningCardProps {
className?: string; className?: string;
showAcknowledgement?: boolean; showAcknowledgement?: boolean;
acknowledged?: boolean; acknowledgementPhrase?: string;
onAcknowledgedChange?: (value: boolean) => void; acknowledgementText?: string;
onAcknowledgementTextChange?: (value: string) => void;
disabled?: boolean; disabled?: boolean;
showProxySettingsLink?: boolean; showProxySettingsLink?: boolean;
} }
@@ -16,8 +18,9 @@ interface AccountSafetyWarningCardProps {
export function AccountSafetyWarningCard({ export function AccountSafetyWarningCard({
className, className,
showAcknowledgement = false, showAcknowledgement = false,
acknowledged = false, acknowledgementPhrase = RISK_ACK_PHRASE,
onAcknowledgedChange, acknowledgementText = '',
onAcknowledgementTextChange,
disabled = false, disabled = false,
showProxySettingsLink = false, showProxySettingsLink = false,
}: AccountSafetyWarningCardProps) { }: AccountSafetyWarningCardProps) {
@@ -99,20 +102,22 @@ export function AccountSafetyWarningCard({
</span> </span>
</div> </div>
{showAcknowledgement && onAcknowledgedChange && ( {showAcknowledgement && onAcknowledgementTextChange && (
<div className="rounded-lg border border-amber-500/25 bg-amber-500/5 p-2.5"> <div className="rounded-lg border border-amber-500/25 bg-amber-500/5 p-2.5">
<div className="flex items-start gap-2"> <Label htmlFor="account-risk-ack-text" className="text-xs leading-5">
<Checkbox Type exact phrase to continue:{' '}
id="account-risk-ack" <code className="rounded bg-muted px-1 py-0.5 font-mono">
checked={acknowledged} {acknowledgementPhrase}
onCheckedChange={(checked) => onAcknowledgedChange(Boolean(checked))} </code>
disabled={disabled} </Label>
/> <Input
<Label htmlFor="account-risk-ack" className="text-xs leading-5"> id="account-risk-ack-text"
I understand this risk and that CCS takes no responsibility if I continue this value={acknowledgementText}
setup. onChange={(e) => onAcknowledgementTextChange(e.target.value)}
</Label> placeholder={acknowledgementPhrase}
</div> disabled={disabled}
className="mt-2 font-mono text-xs"
/>
</div> </div>
)} )}
</div> </div>
@@ -34,6 +34,7 @@ import { AntigravityResponsibilityChecklist } from '@/components/account/antigra
import { import {
ANTIGRAVITY_ACK_VERSION, ANTIGRAVITY_ACK_VERSION,
DEFAULT_ANTIGRAVITY_RISK_CHECKLIST, DEFAULT_ANTIGRAVITY_RISK_CHECKLIST,
RISK_ACK_PHRASE,
isAntigravityRiskChecklistComplete, isAntigravityRiskChecklistComplete,
} from '@/components/account/antigravity-responsibility-constants'; } from '@/components/account/antigravity-responsibility-constants';
import { import {
@@ -55,6 +56,10 @@ interface AddAccountDialogProps {
isFirstAccount?: boolean; isFirstAccount?: boolean;
} }
function normalizeRiskPhrase(value: string): string {
return value.trim().replace(/\s+/g, ' ').toUpperCase();
}
export function AddAccountDialog({ export function AddAccountDialog({
open, open,
onClose, onClose,
@@ -66,7 +71,7 @@ export function AddAccountDialog({
const [callbackUrl, setCallbackUrl] = useState(''); const [callbackUrl, setCallbackUrl] = useState('');
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const [localError, setLocalError] = useState<string | null>(null); const [localError, setLocalError] = useState<string | null>(null);
const [acknowledgedRisk, setAcknowledgedRisk] = useState(false); const [riskAcknowledgementText, setRiskAcknowledgementText] = useState('');
const [agyRiskChecklist, setAgyRiskChecklist] = useState(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST); const [agyRiskChecklist, setAgyRiskChecklist] = useState(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST);
const [agyAckBypassEnabled, setAgyAckBypassEnabled] = useState(false); const [agyAckBypassEnabled, setAgyAckBypassEnabled] = useState(false);
const [agyAckBypassLoading, setAgyAckBypassLoading] = useState(false); const [agyAckBypassLoading, setAgyAckBypassLoading] = useState(false);
@@ -80,6 +85,7 @@ export function AddAccountDialog({
const requiresAgyResponsibilityFlow = provider === 'agy' && !agyAckBypassEnabled; const requiresAgyResponsibilityFlow = provider === 'agy' && !agyAckBypassEnabled;
const isAgyBypassStatePending = provider === 'agy' && agyAckBypassLoading; const isAgyBypassStatePending = provider === 'agy' && agyAckBypassLoading;
const isAgyRiskChecklistComplete = isAntigravityRiskChecklistComplete(agyRiskChecklist); const isAgyRiskChecklistComplete = isAntigravityRiskChecklistComplete(agyRiskChecklist);
const isGeminiRiskAcknowledged = normalizeRiskPhrase(riskAcknowledgementText) === RISK_ACK_PHRASE;
const defaultDeviceCode = isDeviceCodeProvider(provider); const defaultDeviceCode = isDeviceCodeProvider(provider);
const requiresNickname = isNicknameRequiredProvider(provider); const requiresNickname = isNicknameRequiredProvider(provider);
const kiroMethodOption = getKiroAuthMethodOption(kiroAuthMethod); const kiroMethodOption = getKiroAuthMethodOption(kiroAuthMethod);
@@ -102,7 +108,7 @@ export function AddAccountDialog({
setCallbackUrl(''); setCallbackUrl('');
setCopied(false); setCopied(false);
setLocalError(null); setLocalError(null);
setAcknowledgedRisk(false); setRiskAcknowledgementText('');
setAgyRiskChecklist(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST); setAgyRiskChecklist(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST);
setAgyAckBypassEnabled(false); setAgyAckBypassEnabled(false);
setAgyAckBypassLoading(false); setAgyAckBypassLoading(false);
@@ -113,7 +119,7 @@ export function AddAccountDialog({
useEffect(() => { useEffect(() => {
if (open) { if (open) {
setAcknowledgedRisk(false); setRiskAcknowledgementText('');
setAgyRiskChecklist(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST); setAgyRiskChecklist(DEFAULT_ANTIGRAVITY_RISK_CHECKLIST);
setLocalError(null); setLocalError(null);
} }
@@ -252,9 +258,9 @@ export function AddAccountDialog({
); );
return; return;
} }
if (requiresSafetyAcknowledgement && !acknowledgedRisk) { if (requiresSafetyAcknowledgement && !isGeminiRiskAcknowledged) {
setLocalError( setLocalError(
'Please acknowledge the account safety warning before authenticating this provider.' `Type "${RISK_ACK_PHRASE}" to acknowledge the account safety warning before authenticating this provider.`
); );
return; return;
} }
@@ -348,9 +354,10 @@ export function AddAccountDialog({
{requiresSafetyAcknowledgement && !showAuthUI && ( {requiresSafetyAcknowledgement && !showAuthUI && (
<AccountSafetyWarningCard <AccountSafetyWarningCard
showAcknowledgement showAcknowledgement
acknowledged={acknowledgedRisk} acknowledgementPhrase={RISK_ACK_PHRASE}
onAcknowledgedChange={(value) => { acknowledgementText={riskAcknowledgementText}
setAcknowledgedRisk(value); onAcknowledgementTextChange={(value) => {
setRiskAcknowledgementText(value);
setLocalError(null); setLocalError(null);
}} }}
disabled={isPending} disabled={isPending}
@@ -549,7 +556,7 @@ export function AddAccountDialog({
isAgyBypassStatePending || isAgyBypassStatePending ||
(requiresNickname && !nicknameTrimmed) || (requiresNickname && !nicknameTrimmed) ||
(requiresAgyResponsibilityFlow && !isAgyRiskChecklistComplete) || (requiresAgyResponsibilityFlow && !isAgyRiskChecklistComplete) ||
(requiresSafetyAcknowledgement && !acknowledgedRisk) (requiresSafetyAcknowledgement && !isGeminiRiskAcknowledged)
} }
> >
<ExternalLink className="w-4 h-4 mr-2" /> <ExternalLink className="w-4 h-4 mr-2" />
@@ -1,5 +1,6 @@
export const ANTIGRAVITY_ACK_VERSION = '2026-02-24-antigravity-oauth-v2'; export const ANTIGRAVITY_ACK_VERSION = '2026-02-24-antigravity-oauth-v2';
export const ANTIGRAVITY_ACK_PHRASE = 'I ACCEPT AGY RISK'; export const RISK_ACK_PHRASE = 'I ACCEPT RISK';
export const ANTIGRAVITY_ACK_PHRASE = RISK_ACK_PHRASE;
export interface AntigravityRiskChecklistValue { export interface AntigravityRiskChecklistValue {
reviewedIssue509: boolean; reviewedIssue509: boolean;