fix: add Zscaler SSL certificate support for ChromaDB vector search#884
fix: add Zscaler SSL certificate support for ChromaDB vector search#884RClark4958 wants to merge 2 commits intothedotmack:mainfrom
Conversation
Detects and combines enterprise security certificates (Zscaler) with standard certifi certificates on macOS. Passes SSL environment variables to chroma-mcp process.
Greptile OverviewGreptile SummaryAdded automatic Zscaler SSL certificate detection and configuration for ChromaDB vector search on macOS to fix SSL verification failures in corporate environments with SSL interception. The implementation:
Potential improvements:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant App as ChromaSync
participant FS as File System
participant KeyChain as macOS Keychain
participant UV as UV Cache
participant ChromaMCP as chroma-mcp (Python)
Note over App: ensureConnection() called
App->>App: getCombinedCertPath()
alt Cached cert exists and < 24h old
App->>FS: Check combined_certs.pem age
FS-->>App: Use cached cert
else Need to create cert bundle
App->>App: Check if platform is darwin
alt On macOS
App->>UV: Scan ~/.cache/uv/archive-v0
UV-->>App: Find certifi/cacert.pem path
App->>KeyChain: execSync("security find-certificate -a -c Zscaler...")
KeyChain-->>App: Return Zscaler certificate
App->>FS: Read certifi cacert.pem
FS-->>App: Standard CA certs content
App->>FS: writeFileSync(combined_certs.pem, certifi + zscaler)
FS-->>App: Combined bundle created
App-->>App: Return combinedCertPath
else Not macOS or Zscaler not found
App-->>App: Return undefined
end
end
App->>App: Configure transportOptions
alt combinedCertPath exists
App->>App: Set env.SSL_CERT_FILE = combinedCertPath
App->>App: Set env.REQUESTS_CA_BUNDLE = combinedCertPath
App->>App: Set env.CURL_CA_BUNDLE = combinedCertPath
end
App->>ChromaMCP: StdioClientTransport.spawn(uvx chroma-mcp) with env vars
Note over ChromaMCP: Python process inherits<br/>SSL environment variables
ChromaMCP-->>App: Connection established
Note over ChromaMCP: HTTPS requests now trust<br/>both standard CAs and Zscaler cert
|
src/services/sync/ChromaSync.ts
Outdated
| const cacheEntries = fs.readdirSync(uvCacheDir); | ||
| for (const entry of cacheEntries) { | ||
| const candidatePath = path.join(uvCacheDir, entry, 'lib'); | ||
| if (fs.existsSync(candidatePath)) { | ||
| const libEntries = fs.readdirSync(candidatePath); | ||
| for (const libEntry of libEntries) { | ||
| if (libEntry.startsWith('python')) { | ||
| const cacertPath = path.join(candidatePath, libEntry, 'site-packages', 'certifi', 'cacert.pem'); | ||
| if (fs.existsSync(cacertPath)) { | ||
| certifiPath = cacertPath; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if (certifiPath) break; | ||
| } |
There was a problem hiding this comment.
brittle certifi path discovery relies on specific uv cache structure
The nested loops scanning ~/.cache/uv/archive-v0 assume a specific directory layout. If uv changes its cache structure, or if multiple Python versions exist, this could pick the wrong certifi or fail silently. The code breaks on the first match without verifying it's the correct version being used by uvx for this specific invocation.
Consider using uvx --with certifi python -c "import certifi; print(certifi.where())" to let uvx resolve the correct certifi path for the exact Python environment it will use.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/sync/ChromaSync.ts
Line: 138:154
Comment:
brittle certifi path discovery relies on specific uv cache structure
The nested loops scanning `~/.cache/uv/archive-v0` assume a specific directory layout. If uv changes its cache structure, or if multiple Python versions exist, this could pick the wrong certifi or fail silently. The code breaks on the first match without verifying it's the correct version being used by `uvx` for this specific invocation.
Consider using `uvx --with certifi python -c "import certifi; print(certifi.where())"` to let uvx resolve the correct certifi path for the exact Python environment it will use.
How can I resolve this? If you propose a fix, please make it concise.
src/services/sync/ChromaSync.ts
Outdated
| const certifiContent = fs.readFileSync(certifiPath, 'utf8'); | ||
| fs.writeFileSync(combinedCertPath, certifiContent + '\n' + zscalerCert); |
There was a problem hiding this comment.
combined certificate file lacks atomic write protection
Writing directly with writeFileSync means if the process crashes mid-write, the cert bundle could be corrupted. Since this file is cached for 24 hours (line 120), a corrupted file would cause SSL failures until the cache expires or is manually deleted.
| const certifiContent = fs.readFileSync(certifiPath, 'utf8'); | |
| fs.writeFileSync(combinedCertPath, certifiContent + '\n' + zscalerCert); | |
| const tempPath = combinedCertPath + '.tmp'; | |
| fs.writeFileSync(tempPath, certifiContent + '\n' + zscalerCert); | |
| fs.renameSync(tempPath, combinedCertPath); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/sync/ChromaSync.ts
Line: 177:178
Comment:
combined certificate file lacks atomic write protection
Writing directly with `writeFileSync` means if the process crashes mid-write, the cert bundle could be corrupted. Since this file is cached for 24 hours (line 120), a corrupted file would cause SSL failures until the cache expires or is manually deleted.
```suggestion
const tempPath = combinedCertPath + '.tmp';
fs.writeFileSync(tempPath, certifiContent + '\n' + zscalerCert);
fs.renameSync(tempPath, combinedCertPath);
```
How can I resolve this? If you propose a fix, please make it concise.| zscalerCert = execSync( | ||
| 'security find-certificate -a -c "Zscaler" -p /Library/Keychains/System.keychain', | ||
| { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] } | ||
| ); |
There was a problem hiding this comment.
consider timeout for security command execution
execSync without a timeout could hang indefinitely if the macOS keychain is locked or unresponsive. This would block the entire ChromaSync initialization.
| zscalerCert = execSync( | |
| 'security find-certificate -a -c "Zscaler" -p /Library/Keychains/System.keychain', | |
| { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] } | |
| ); | |
| zscalerCert = execSync( | |
| 'security find-certificate -a -c "Zscaler" -p /Library/Keychains/System.keychain', | |
| { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000 } | |
| ); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/sync/ChromaSync.ts
Line: 163:166
Comment:
consider timeout for security command execution
`execSync` without a timeout could hang indefinitely if the macOS keychain is locked or unresponsive. This would block the entire ChromaSync initialization.
```suggestion
zscalerCert = execSync(
'security find-certificate -a -c "Zscaler" -p /Library/Keychains/System.keychain',
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000 }
);
```
How can I resolve this? If you propose a fix, please make it concise.
src/services/sync/ChromaSync.ts
Outdated
| if (!zscalerCert || !zscalerCert.includes('BEGIN CERTIFICATE')) { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
weak certificate validation
Checking only for 'BEGIN CERTIFICATE' substring doesn't validate certificate format or integrity. Malformed certificates would pass this check and potentially cause SSL errors later.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/services/sync/ChromaSync.ts
Line: 172:174
Comment:
weak certificate validation
Checking only for `'BEGIN CERTIFICATE'` substring doesn't validate certificate format or integrity. Malformed certificates would pass this check and potentially cause SSL errors later.
How can I resolve this? If you propose a fix, please make it concise.- Use uvx to resolve certifi path instead of scanning cache - Add timeouts to execSync calls (5s/10s) - Validate full PEM format (BEGIN + END markers) - Atomic file write (temp file + rename)
|
Manually merged into main with conflict resolution (compiled output conflicts from PR #769 merge). Source changes applied cleanly. All 39 Chroma tests pass. Build clean. Thank you @RClark4958 for the enterprise SSL support! |
…omaDB vector search Adds automatic detection and handling of Zscaler enterprise security certificates on macOS. Combines standard certifi CA certificates with Zscaler certificates into a single bundle, passed via SSL_CERT_FILE/REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE env vars to the chroma-mcp subprocess. Certificate bundle is cached for 24 hours. Falls back gracefully when Zscaler is not present, with no impact on non-Zscaler environments. Co-Authored-By: RClark4958 <rickdclark48@gmail.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ge notes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Problem
In enterprise environments with Zscaler, HTTPS traffic is intercepted and re-signed with Zscaler's certificate. Python's SSL verification fails because it doesn't trust the Zscaler certificate, causing all ChromaDB operations (add/query) to fail with
SSL: CERTIFICATE_VERIFY_FAILED.Solution
getCombinedCertPath()method that automatically detects Zscaler certificates in the macOS system keychainSSL_CERT_FILE,REQUESTS_CA_BUNDLE,CURL_CA_BUNDLE) to the chroma-mcp processTest plan