areibman / areibman/bottleneck
Feature: Add clear security notice about local-only token storage with source code proof
- Dominant language
- TypeScript
- Stars
- 156
- Forks
- 21
- PR merge metrics
- No merged PRs in 30d
Description
## Feature Request
Add prominent security notices and documentation that clearly explains personal access tokens are stored locally only and never uploaded to any server, including direct links to the source code that proves this.
## Description
Users need absolute confidence that their GitHub personal access tokens are secure. We should provide clear, transparent communication about token storage with direct links to the source code that demonstrates tokens never leave the user's machine.
## Core Features
### 1. Security Notice UI
#### Token Input Screen
```jsx
function TokenInputScreen() {
return (
π Your Security is Our Priority
πΎ
Stored Locally Only
Your token is encrypted and stored on YOUR device only
View source code β
π Technical Details (click to expand)
Storage Implementation:
// src/main/storage/secure-storage.ts:45
async function storeToken(token: string): Promise {
const encrypted = await keytar.setPassword(
'bottleneck',
'github-token',
token
);
// Token is stored in OS keychain, never sent anywhere
}
Network Requests:
// src/main/api/github-client.ts:12
class GitHubClient {
constructor(token: string) {
// Token is only used for GitHub API
this.octokit = new Octokit({
auth: token,
baseURL: 'https://api.github.com' // ONLY GitHub
});
}
}
What we DO NOT do:
- β Send tokens to analytics
- β Include tokens in error reports
- β Store tokens in plain text
- β Share tokens between devices
- β Access tokens without user action
Save Token Locally
);
}
```
### 2. Settings Page Security Section
```jsx
function SecuritySettings() {
return (
Security & Privacy
Token Storage
Token stored in: {getKeychainLocation()}
Encryption: AES-256-GCM
Last accessed: {lastAccessTime}
π View Storage Location
π Verify No External Calls
π Export Security Audit
Privacy Report
External API Calls
GitHub API only
View source
Analytics/Telemetry
None
Verify
Token Transmission
Never leaves device
View code
Data Collection
None
Confirm
);
}
```
### 3. Source Code Documentation
#### secure-storage.ts
```typescript
/**
* SECURITY NOTICE: Token Storage
* ================================
* This file handles the secure storage of GitHub tokens.
*
* IMPORTANT: Tokens are ONLY stored locally using the OS keychain:
* - macOS: Keychain Access
* - Windows: Credential Manager
* - Linux: Secret Service API / libsecret
*
* Tokens are NEVER:
* - Sent to external servers
* - Included in logs or error reports
* - Stored in plain text
* - Accessible without user authentication
*
* For security audit, see: docs/SECURITY.md
*/
import * as keytar from 'keytar';
import { safeStorage } from 'electron';
const SERVICE_NAME = 'bottleneck';
const ACCOUNT_NAME = 'github-token';
export class SecureTokenStorage {
/**
* Stores token in OS keychain - LOCAL ONLY
* This function NEVER makes network requests
*/
async storeToken(token: string): Promise {
// Validate token format
if (!token.startsWith('ghp_') && !token.startsWith('github_pat_')) {
throw new Error('Invalid token format');
}
// Encrypt if additional encryption is enabled
const encrypted = safeStorage.isEncryptionAvailable()
? safeStorage.encryptString(token)
: token;
// Store in OS keychain (LOCAL ONLY)
await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, encrypted);
// Log storage event (no token data)
this.logSecurityEvent('TOKEN_STORED', {
timestamp: Date.now(),
method: 'keychain',
// NEVER log the actual token
});
}
/**
* Retrieves token from LOCAL storage only
*/
async getToken(): Promise {
const encrypted = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
if (!encrypted) return null;
return safeStorage.isEncryptionAvailable()
? safeStorage.decryptString(Buffer.from(encrypted))
: encrypted;
}
/**
* Removes token from local storage
*/
async deleteToken(): Promise {
await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
}
}
```
#### github-api.ts
```typescript
/**
* SECURITY NOTICE: API Communication
* ===================================
* This file handles ALL network communication.
*
* IMPORTANT: We ONLY communicate with GitHub's API.
*
* NO requests are made to:
* - Our servers (we don't have any)
* - Analytics services
* - Third-party services
* - Telemetry endpoints
*
* You can verify this by:
* 1. Searching the codebase for fetch() and axios calls
* 2. Monitoring network traffic while using the app
* 3. Checking the CSP headers in main/security.ts
*/
import { Octokit } from '@octokit/rest';
export class GitHubAPI {
private octokit: Octokit;
constructor(token: string) {
// Token is ONLY used for GitHub API authentication
this.octokit = new Octokit({
auth: token,
baseUrl: 'https://api.github.com', // ONLY GitHub
// Prevent token leakage in logs
log: {
debug: () => {},
info: () => {},
warn: console.warn,
error: (msg) => {
// Sanitize error messages to remove token
const sanitized = msg.replace(/ghp_[a-zA-Z0-9]{36}/g, '[REDACTED]');
console.error(sanitized);
}
}
});
}
// All API methods only call GitHub...
}
```
### 4. README Security Section
```markdown
## π Security & Privacy
### Your Token is Safe
**We take your security seriously.** Your GitHub personal access token is:
- β
**Stored locally only** - Never leaves your device
- β
**Encrypted** - Using your operating system's secure keychain
- β
**Never uploaded** - We have no servers to send it to
- β
**Open source** - Verify our claims yourself
### Verify Our Security Claims
Don't just trust us - verify it yourself:
1. **Check Token Storage:** [`src/main/storage/secure-storage.ts`](https://github.com/areibman/bottleneck/blob/main/src/main/storage/secure-storage.ts#L45)
2. **Check API Calls:** [`src/main/api/github-client.ts`](https://github.com/areibman/bottleneck/blob/main/src/main/api/github-client.ts)
3. **Search for Network Calls:** [Search: `fetch` OR `axios` OR `request`](https://github.com/areibman/bottleneck/search?q=fetch+OR+axios+OR+request)
4. **Monitor Network Traffic:** Use your browser's DevTools to verify no external calls
### Security Architecture
```
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Your Token ββββββΆβ OS Keychain ββββββΆβ GitHub API β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
(Local Storage) (Only External)
β
β No connection to:
- Our servers (don't exist)
- Analytics
- Third parties
```
### FAQ
**Q: Can you see my token?**
A: No. It's stored locally on your device only.
**Q: What if I don't trust you?**
A: Great! Check our source code. It's all open source.
**Q: Where exactly is my token stored?**
- macOS: `~/Library/Keychains/`
- Windows: Windows Credential Manager
- Linux: GNOME Keyring / KWallet
**Q: Can I verify no network calls are made?**
A: Yes! Open DevTools Network tab and monitor all requests.
```
### 5. In-App Verification Tool
```javascript
class SecurityVerifier {
async runSecurityAudit() {
const report = {
tokenStorage: await this.verifyTokenStorage(),
networkCalls: await this.verifyNetworkCalls(),
codeIntegrity: await this.verifyCodeIntegrity(),
permissions: await this.verifyPermissions()
};
return {
passed: Object.values(report).every(r => r.passed),
report,
timestamp: Date.now()
};
}
async verifyTokenStorage() {
// Check token is in keychain
const locations = this.getKeychainLocations();
const stored = await this.checkTokenLocation();
return {
passed: stored.isLocal && !stored.isAccessibleExternally,
location: locations[process.platform],
encrypted: true,
details: 'Token found in OS keychain only'
};
}
async verifyNetworkCalls() {
// Monitor all network requests
const requests = await this.interceptAllRequests();
const externalCalls = requests.filter(r =>
!r.url.includes('github.com') &&
!r.url.includes('localhost')
);
return {
passed: externalCalls.length === 0,
githubOnly: requests.every(r => r.url.includes('github.com')),
externalCalls,
details: `${requests.length} calls to GitHub API only`
};
}
}
```
### 6. Build-Time Security Checks
```javascript
// scripts/security-audit.js
/**
* Pre-build security audit
* Ensures no accidental token leakage code is added
*/
const securityPatterns = [
// Flag any external API calls
{
pattern: /fetch\(['"](?!https:\/\/api\.github\.com)/,
message: 'External API call detected'
},
// Flag analytics/telemetry
{
pattern: /google-analytics|segment|mixpanel|amplitude/i,
message: 'Analytics library detected'
},
// Flag token transmission
{
pattern: /token.*fetch|axios.*token|request.*token/i,
message: 'Possible token transmission detected'
}
];
function auditSecurity() {
// Scan all source files
const violations = scanForViolations(securityPatterns);
if (violations.length > 0) {
console.error('β Security audit failed!');
violations.forEach(v => console.error(v));
process.exit(1);
}
console.log('β
Security audit passed');
}
```
## Benefits
- **User Trust**: Complete transparency about token security
- **Verifiable Claims**: Users can verify security themselves
- **Open Source Confidence**: Direct links to source code
- **Privacy First**: Clear commitment to user privacy
- **No Black Box**: Everything is auditable
## Acceptance Criteria
- [ ] Security notice prominently displayed on token input
- [ ] Source code links work and point to correct lines
- [ ] README includes comprehensive security section
- [ ] In-app security audit tool works
- [ ] Network monitor shows only GitHub API calls
- [ ] Token storage location is displayed
- [ ] Build-time security checks implemented
- [ ] Privacy report is accurate and updated
- [ ] FAQ addresses common concerns
- [ ] Documentation is clear and accessible
- [ ] Security claims are verifiable by users
π€ Generated with [Claude Code](https://claude.ai/code)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by checking the existing token-input and settings entry points, then read src/main/storage/secure-storage.ts, src/main/api/github-client.ts, github-api.ts, main/security.ts, and the README. Compare the current implementation with the requested notices, source links, documentation, and verification tool; done means the agreed security claims are accurately presented and auditable without unsupported assertions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, github, typescript
- Domain
- documentation, frontend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100