/ utils / secureStorage / plainTextStorage.ts
plainTextStorage.ts
 1  import { chmodSync } from 'fs'
 2  import { join } from 'path'
 3  import { getClaudeConfigHomeDir } from '../envUtils.js'
 4  import { getErrnoCode } from '../errors.js'
 5  import { getFsImplementation } from '../fsOperations.js'
 6  import {
 7    jsonParse,
 8    jsonStringify,
 9    writeFileSync_DEPRECATED,
10  } from '../slowOperations.js'
11  import type { SecureStorage, SecureStorageData } from './types.js'
12  
13  function getStoragePath(): { storageDir: string; storagePath: string } {
14    const storageDir = getClaudeConfigHomeDir()
15    const storageFileName = '.credentials.json'
16    return { storageDir, storagePath: join(storageDir, storageFileName) }
17  }
18  
19  export const plainTextStorage = {
20    name: 'plaintext',
21    read(): SecureStorageData | null {
22      // sync IO: called from sync context (SecureStorage interface)
23      const { storagePath } = getStoragePath()
24      try {
25        const data = getFsImplementation().readFileSync(storagePath, {
26          encoding: 'utf8',
27        })
28        return jsonParse(data)
29      } catch {
30        return null
31      }
32    },
33    async readAsync(): Promise<SecureStorageData | null> {
34      const { storagePath } = getStoragePath()
35      try {
36        const data = await getFsImplementation().readFile(storagePath, {
37          encoding: 'utf8',
38        })
39        return jsonParse(data)
40      } catch {
41        return null
42      }
43    },
44    update(data: SecureStorageData): { success: boolean; warning?: string } {
45      // sync IO: called from sync context (SecureStorage interface)
46      try {
47        const { storageDir, storagePath } = getStoragePath()
48        try {
49          getFsImplementation().mkdirSync(storageDir)
50        } catch (e: unknown) {
51          const code = getErrnoCode(e)
52          if (code !== 'EEXIST') {
53            throw e
54          }
55        }
56  
57        writeFileSync_DEPRECATED(storagePath, jsonStringify(data), {
58          encoding: 'utf8',
59          flush: false,
60        })
61        chmodSync(storagePath, 0o600)
62        return {
63          success: true,
64          warning: 'Warning: Storing credentials in plaintext.',
65        }
66      } catch {
67        return { success: false }
68      }
69    },
70    delete(): boolean {
71      // sync IO: called from sync context (SecureStorage interface)
72      const { storagePath } = getStoragePath()
73      try {
74        getFsImplementation().unlinkSync(storagePath)
75        return true
76      } catch (e: unknown) {
77        const code = getErrnoCode(e)
78        if (code === 'ENOENT') {
79          return true
80        }
81        return false
82      }
83    },
84  } satisfies SecureStorage