@google/gemini-cli-core 0.54.0-nightly.20260727.g3818efbbf → 0.54.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
+ import { promises as fs } from 'node:fs';
8
+ import * as path from 'node:path';
9
+ import * as os from 'node:os';
10
+ import * as crypto from 'node:crypto';
11
+ import { FileKeychain } from './fileKeychain.js';
12
+ describe('AES-GCM Tag Length Verification', () => {
13
+ let tempDir;
14
+ beforeEach(async () => {
15
+ // Create a unique temporary directory for test isolation
16
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gemini-test-keychain-'));
17
+ vi.stubEnv('GEMINI_CLI_HOME', tempDir);
18
+ });
19
+ afterEach(async () => {
20
+ vi.unstubAllEnvs();
21
+ // Clean up the temporary directory
22
+ await fs.rm(tempDir, { recursive: true, force: true });
23
+ });
24
+ it('should use a secure 128-bit (16-byte) AES-GCM authentication tag and standard 12-byte IV', async () => {
25
+ const keychain = new FileKeychain();
26
+ const service = 'test-service';
27
+ const account = 'test-account';
28
+ const password = 'secure-password-123';
29
+ // 1. Save credentials to trigger encryption and file write
30
+ await keychain.setPassword(service, account, password);
31
+ // 2. Read the raw encrypted file from disk
32
+ const credentialsFilePath = path.join(tempDir, '.gemini', 'gemini-credentials.json');
33
+ const rawEncryptedData = await fs.readFile(credentialsFilePath, 'utf-8');
34
+ // 3. Parse the encrypted data format (iv:authTag:encrypted)
35
+ const parts = rawEncryptedData.split(':');
36
+ expect(parts).toHaveLength(3);
37
+ const ivHex = parts[0];
38
+ const authTagHex = parts[1];
39
+ // 4. Verify the lengths of the components
40
+ const ivBuffer = Buffer.from(ivHex, 'hex');
41
+ const authTagBuffer = Buffer.from(authTagHex, 'hex');
42
+ // IV should be exactly 12 bytes (96 bits) by default
43
+ expect(ivBuffer.length).toBe(12);
44
+ expect(ivHex.length).toBe(24);
45
+ // Authentication Tag should be exactly 16 bytes (128 bits)
46
+ expect(authTagBuffer.length).toBe(16);
47
+ expect(authTagHex.length).toBe(32); // 32 hex characters
48
+ // Assert that the tag is NOT truncated to 4 bytes (32 bits)
49
+ expect(authTagBuffer.length).not.toBe(4);
50
+ expect(authTagHex.length).not.toBe(8); // 8 hex characters
51
+ // 5. Verify that decryption works correctly with the 16-byte tag
52
+ const decryptedPassword = await keychain.getPassword(service, account);
53
+ expect(decryptedPassword).toBe(password);
54
+ });
55
+ it('should support both 12-byte and 16-byte IVs for backward compatibility', async () => {
56
+ const keychain = new FileKeychain();
57
+ const service = 'test-service';
58
+ const account = 'test-account';
59
+ const password = 'secure-password-123';
60
+ // 1. Save credentials to trigger encryption and file write (generates 12-byte IV)
61
+ await keychain.setPassword(service, account, password);
62
+ // 2. Verify 12-byte IV decryption works
63
+ let decryptedPassword = await keychain.getPassword(service, account);
64
+ expect(decryptedPassword).toBe(password);
65
+ // 3. Manually simulate a legacy 16-byte IV credentials file
66
+ const credentialsFilePath = path.join(tempDir, '.gemini', 'gemini-credentials.json');
67
+ const legacyIv = crypto.randomBytes(16);
68
+ const encryptionKey = keychain
69
+ .encryptionKey;
70
+ const cipher = crypto.createCipheriv('aes-256-gcm', encryptionKey, legacyIv, {
71
+ authTagLength: 16,
72
+ });
73
+ let encrypted = cipher.update(JSON.stringify({ [service]: { [account]: password } }), 'utf8', 'hex');
74
+ encrypted += cipher.final('hex');
75
+ const authTag = cipher.getAuthTag();
76
+ const legacyPayload = legacyIv.toString('hex') +
77
+ ':' +
78
+ authTag.toString('hex') +
79
+ ':' +
80
+ encrypted;
81
+ await fs.writeFile(credentialsFilePath, legacyPayload, 'utf-8');
82
+ // 4. Verify 16-byte IV decryption works successfully (backward compatibility)
83
+ decryptedPassword = await keychain.getPassword(service, account);
84
+ expect(decryptedPassword).toBe(password);
85
+ });
86
+ it('should reject decryption of a credentials file with a truncated tag', async () => {
87
+ const keychain = new FileKeychain();
88
+ const service = 'test-service';
89
+ const account = 'test-account';
90
+ const password = 'secure-password-123';
91
+ // 1. Save credentials to trigger encryption and file write
92
+ await keychain.setPassword(service, account, password);
93
+ // 2. Read the raw encrypted file from disk
94
+ const credentialsFilePath = path.join(tempDir, '.gemini', 'gemini-credentials.json');
95
+ const rawEncryptedData = await fs.readFile(credentialsFilePath, 'utf-8');
96
+ // 3. Parse the encrypted data format (iv:authTag:encrypted)
97
+ const parts = rawEncryptedData.split(':');
98
+ expect(parts).toHaveLength(3);
99
+ const ivHex = parts[0];
100
+ const authTagHex = parts[1];
101
+ const encryptedHex = parts[2];
102
+ // 4. Create a truncated 4-byte tag (8 hex characters)
103
+ const truncatedTagHex = authTagHex.substring(0, 8);
104
+ const truncatedEncryptedData = `${ivHex}:${truncatedTagHex}:${encryptedHex}`;
105
+ // 5. Overwrite the credentials file with the truncated-tag payload
106
+ await fs.writeFile(credentialsFilePath, truncatedEncryptedData, 'utf-8');
107
+ // 6. Attempt to retrieve the password and verify it throws a clear, handled validation error
108
+ await expect(keychain.getPassword(service, account)).rejects.toThrow('Corrupted credentials file detected');
109
+ });
110
+ it('should reject decryption of a credentials file with a truncated IV', async () => {
111
+ const keychain = new FileKeychain();
112
+ const service = 'test-service';
113
+ const account = 'test-account';
114
+ const password = 'secure-password-123';
115
+ // 1. Save credentials to trigger encryption and file write
116
+ await keychain.setPassword(service, account, password);
117
+ // 2. Read the raw encrypted file from disk
118
+ const credentialsFilePath = path.join(tempDir, '.gemini', 'gemini-credentials.json');
119
+ const rawEncryptedData = await fs.readFile(credentialsFilePath, 'utf-8');
120
+ // 3. Parse the encrypted data format (iv:authTag:encrypted)
121
+ const parts = rawEncryptedData.split(':');
122
+ expect(parts).toHaveLength(3);
123
+ const ivHex = parts[0];
124
+ const authTagHex = parts[1];
125
+ const encryptedHex = parts[2];
126
+ // 4. Create a truncated 4-byte IV (8 hex characters)
127
+ const truncatedIvHex = ivHex.substring(0, 8);
128
+ const truncatedEncryptedData = `${truncatedIvHex}:${authTagHex}:${encryptedHex}`;
129
+ // 5. Overwrite the credentials file with the truncated-IV payload
130
+ await fs.writeFile(credentialsFilePath, truncatedEncryptedData, 'utf-8');
131
+ // 6. Attempt to retrieve the password and verify it throws a clear, handled validation error
132
+ await expect(keychain.getPassword(service, account)).rejects.toThrow('Corrupted credentials file detected');
133
+ });
134
+ });
135
+ //# sourceMappingURL=gcmTruncationReproduction.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcmTruncationReproduction.test.js","sourceRoot":"","sources":["../../../src/services/gcmTruncationReproduction.test.ts"],"names":[],"mappings":"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"}