envlockr 1.0.0__tar.gz
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.
- envlockr-1.0.0/LICENSE +21 -0
- envlockr-1.0.0/PKG-INFO +241 -0
- envlockr-1.0.0/README.md +200 -0
- envlockr-1.0.0/envlockr.egg-info/PKG-INFO +241 -0
- envlockr-1.0.0/envlockr.egg-info/SOURCES.txt +10 -0
- envlockr-1.0.0/envlockr.egg-info/dependency_links.txt +1 -0
- envlockr-1.0.0/envlockr.egg-info/entry_points.txt +2 -0
- envlockr-1.0.0/envlockr.egg-info/requires.txt +9 -0
- envlockr-1.0.0/envlockr.egg-info/top_level.txt +1 -0
- envlockr-1.0.0/envlockr.py +504 -0
- envlockr-1.0.0/pyproject.toml +83 -0
- envlockr-1.0.0/setup.cfg +4 -0
envlockr-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rohan Ratwani
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
envlockr-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: envlockr
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Secure your environment variables — locally, encrypted, and stream-safe.
|
|
5
|
+
Author-email: Rohan Ratwani <rohanratwani@example.com>
|
|
6
|
+
Maintainer-email: Rohan Ratwani <rohanratwani@example.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://envlockr.netlify.app
|
|
9
|
+
Project-URL: Documentation, https://github.com/RohanRatwani/envlockr-cli#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/RohanRatwani/envlockr-cli
|
|
11
|
+
Project-URL: Issues, https://github.com/RohanRatwani/envlockr-cli/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/RohanRatwani/envlockr-cli/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: environment-variables,secrets,encryption,security,cli,env,dotenv,secrets-management,local-first,developer-tools
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Topic :: Security
|
|
27
|
+
Classifier: Topic :: Security :: Cryptography
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
+
Classifier: Topic :: Utilities
|
|
30
|
+
Requires-Python: >=3.7
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: cryptography>=3.4.0
|
|
34
|
+
Provides-Extra: clipboard
|
|
35
|
+
Requires-Dist: pyperclip>=1.8.0; extra == "clipboard"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: flake8>=4.0.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<img src="banner.png" alt="EnvLockr - Secure Your Environment Variables Locally" />
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
# 🔐 EnvLockr CLI
|
|
47
|
+
|
|
48
|
+
[](https://pypi.org/project/envlockr/)
|
|
49
|
+
[](https://opensource.org/licenses/MIT)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](https://github.com/RohanRatwani/envlockr-cli/stargazers)
|
|
52
|
+
|
|
53
|
+
> Secure your environment variables — locally, encrypted, and stream-safe.
|
|
54
|
+
|
|
55
|
+
EnvLockr CLI is a tool for developers, streamers, and indie hackers who want full control of their secrets without relying on cloud services.
|
|
56
|
+
|
|
57
|
+
## ✨ Features
|
|
58
|
+
|
|
59
|
+
- **Local-first**: All secrets stored securely on your machine
|
|
60
|
+
- **AES Encryption**: Protects your secrets even if your disk is compromised
|
|
61
|
+
- **Offline Mode**: No internet needed to operate
|
|
62
|
+
- **Stream-Safe**: No .env file leaks on screen while coding or streaming
|
|
63
|
+
- **Simple CLI**: Add, copy, list, and retrieve secrets instantly
|
|
64
|
+
- **Cross-Project Friendly**: Works with React, Node.js, Python, and more
|
|
65
|
+
- **GitHub Action**: Automatically scan commits for exposed secrets ([Learn more](GITHUB_ACTION.md))
|
|
66
|
+
|
|
67
|
+
## 🚀 Quick Start
|
|
68
|
+
|
|
69
|
+
### 1. Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Install from PyPI (recommended)
|
|
73
|
+
pip install envlockr
|
|
74
|
+
|
|
75
|
+
# With clipboard support
|
|
76
|
+
pip install envlockr[clipboard]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
<details>
|
|
80
|
+
<summary>Alternative: Install from source</summary>
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/RohanRatwani/envlockr-cli.git
|
|
84
|
+
cd envlockr-cli
|
|
85
|
+
pip install -e .
|
|
86
|
+
```
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
### 2. Commands
|
|
90
|
+
|
|
91
|
+
| Command | Example | What it Does |
|
|
92
|
+
|---------|---------|-------------|
|
|
93
|
+
| add | `envlockr add STRIPE_KEY` | Add a new secret |
|
|
94
|
+
| get | `envlockr get STRIPE_KEY` | Retrieve a secret |
|
|
95
|
+
| list | `envlockr list` | List all stored secrets |
|
|
96
|
+
| copy | `envlockr copy STRIPE_KEY` | Copy secret to clipboard |
|
|
97
|
+
| update | `envlockr update STRIPE_KEY` | Update an existing secret |
|
|
98
|
+
| delete | `envlockr delete STRIPE_KEY` | Delete a secret |
|
|
99
|
+
| export | `envlockr export --output .env` | Export all secrets to .env file |
|
|
100
|
+
| import | `envlockr import .env` | Import secrets from .env file |
|
|
101
|
+
| --version | `envlockr --version` | Show version number |
|
|
102
|
+
## ⚡ How to Use in Your Projects
|
|
103
|
+
|
|
104
|
+
### 🖥 Node.js / React / Vite / Next.js
|
|
105
|
+
|
|
106
|
+
#### Option 1: Export to .env file
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
envlockr export --output .env
|
|
110
|
+
npm run dev
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Option 2: Inline Injection (no .env needed)
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
export STRIPE_KEY=$(envlockr get STRIPE_KEY)
|
|
117
|
+
npm run dev
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Option 3: Import existing .env file
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Migrate your existing .env to encrypted storage
|
|
124
|
+
envlockr import .env
|
|
125
|
+
rm .env # Delete the unencrypted file
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 🛠 Compatible with
|
|
129
|
+
|
|
130
|
+
- create-react-app
|
|
131
|
+
- Next.js
|
|
132
|
+
- Vite
|
|
133
|
+
- Remix
|
|
134
|
+
- Express
|
|
135
|
+
- NestJS
|
|
136
|
+
- and more!
|
|
137
|
+
|
|
138
|
+
👉 **[See framework-specific examples](EXAMPLES.md)** - React, Next.js, Python, Docker, and more!
|
|
139
|
+
|
|
140
|
+
## 📦 Local Storage
|
|
141
|
+
|
|
142
|
+
All your secrets are encrypted and stored in:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
~/.envlockr/vault.json
|
|
146
|
+
~/.envlockr/key.key
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- ✅ AES-256 level security
|
|
150
|
+
- ✅ No external cloud or server dependency
|
|
151
|
+
|
|
152
|
+
## � GitHub Action - Prevent Secret Leaks
|
|
153
|
+
|
|
154
|
+
EnvLockr includes a **GitHub Action** that automatically scans your repository for exposed secrets!
|
|
155
|
+
|
|
156
|
+
### Quick Setup
|
|
157
|
+
|
|
158
|
+
Add this to `.github/workflows/envlockr-scan.yml`:
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
name: EnvLockr Secret Scan
|
|
162
|
+
on: [pull_request, push]
|
|
163
|
+
|
|
164
|
+
jobs:
|
|
165
|
+
scan:
|
|
166
|
+
runs-on: ubuntu-latest
|
|
167
|
+
steps:
|
|
168
|
+
- uses: actions/checkout@v4
|
|
169
|
+
- name: Scan for secrets
|
|
170
|
+
run: |
|
|
171
|
+
curl -sSL https://raw.githubusercontent.com/RohanRatwani/envlockr-cli/main/.github/scripts/scan-secrets.sh | bash
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**What it does:**
|
|
175
|
+
- ✅ Scans commits for API keys, tokens, and passwords
|
|
176
|
+
- ✅ Blocks PRs if secrets are detected
|
|
177
|
+
- ✅ Posts helpful comments with fix instructions
|
|
178
|
+
- ✅ Detects AWS, Stripe, GitHub, Google, OpenAI, and more
|
|
179
|
+
|
|
180
|
+
👉 [Full GitHub Action Documentation](GITHUB_ACTION.md)
|
|
181
|
+
|
|
182
|
+
## Why EnvLockr?
|
|
183
|
+
|
|
184
|
+
- 🔒 **Protect your keys** without trusting the cloud
|
|
185
|
+
- 🎥 **Stream coding sessions** without leaking environment secrets
|
|
186
|
+
- ⚡ **Speed up local development** with simple, fast secret management
|
|
187
|
+
|
|
188
|
+
## 📚 Documentation
|
|
189
|
+
|
|
190
|
+
- **[Quick Reference Guide](QUICKSTART.md)** - Common commands and workflows
|
|
191
|
+
- **[Framework Examples](EXAMPLES.md)** - React, Next.js, Python, Node.js, Docker
|
|
192
|
+
- **[GitHub Action Setup](GITHUB_ACTION.md)** - Prevent secret leaks in CI/CD
|
|
193
|
+
- **[Contributing Guide](CONTRIBUTING.md)** - Help improve EnvLockr
|
|
194
|
+
- **[Changelog](CHANGELOG.md)** - Version history
|
|
195
|
+
|
|
196
|
+
## ☕ Support the Project
|
|
197
|
+
|
|
198
|
+
If EnvLockr helps you, consider supporting its development:
|
|
199
|
+
|
|
200
|
+
<a href="https://www.buymeacoffee.com/rohanratwani" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 40px !important;width: 145px !important;" ></a>
|
|
201
|
+
|
|
202
|
+
Or [become a GitHub Sponsor](https://github.com/sponsors/RohanRatwani)
|
|
203
|
+
|
|
204
|
+
## 📥 What's Next?
|
|
205
|
+
|
|
206
|
+
We're exploring new features for EnvLockr, including:
|
|
207
|
+
- Advanced environment injection
|
|
208
|
+
- Project-specific vaults
|
|
209
|
+
- Desktop UI app
|
|
210
|
+
- IDE integrations (VS Code, JetBrains)
|
|
211
|
+
- Team collaboration features
|
|
212
|
+
|
|
213
|
+
👉 [Join the Waitlist](https://envlockr.netlify.app/) and share your thoughts!
|
|
214
|
+
|
|
215
|
+
## 💡 We Want Your Feedback!
|
|
216
|
+
|
|
217
|
+
Which features would you love to see in EnvLockr?
|
|
218
|
+
- 💬 [GitHub Discussions](https://github.com/RohanRatwani/envlockr-cli/discussions)
|
|
219
|
+
- 🐛 [Report Issues](https://github.com/RohanRatwani/envlockr-cli/issues)
|
|
220
|
+
- 🌟 [Star us on GitHub](https://github.com/RohanRatwani/envlockr-cli)
|
|
221
|
+
|
|
222
|
+
## 📚 Documentation
|
|
223
|
+
|
|
224
|
+
- [Quick Reference Guide](QUICKSTART.md) - Common commands and workflows
|
|
225
|
+
- [GitHub Action Setup](GITHUB_ACTION.md) - Prevent secret leaks in CI/CD
|
|
226
|
+
- [Contributing Guide](CONTRIBUTING.md) - Help improve EnvLockr
|
|
227
|
+
- [Changelog](CHANGELOG.md) - Version history
|
|
228
|
+
|
|
229
|
+
## 🛡 License
|
|
230
|
+
|
|
231
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
<p align="center">
|
|
236
|
+
Made with ❤️ by <a href="https://github.com/RohanRatwani">Rohan Ratwani</a>
|
|
237
|
+
<br>
|
|
238
|
+
<a href="https://envlockr.netlify.app">Website</a> ·
|
|
239
|
+
<a href="https://github.com/RohanRatwani/envlockr-cli">GitHub</a> ·
|
|
240
|
+
<a href="https://twitter.com/envlockr">Twitter</a>
|
|
241
|
+
</p>
|
envlockr-1.0.0/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="banner.png" alt="EnvLockr - Secure Your Environment Variables Locally" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# 🔐 EnvLockr CLI
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/envlockr/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://www.python.org/downloads/)
|
|
10
|
+
[](https://github.com/RohanRatwani/envlockr-cli/stargazers)
|
|
11
|
+
|
|
12
|
+
> Secure your environment variables — locally, encrypted, and stream-safe.
|
|
13
|
+
|
|
14
|
+
EnvLockr CLI is a tool for developers, streamers, and indie hackers who want full control of their secrets without relying on cloud services.
|
|
15
|
+
|
|
16
|
+
## ✨ Features
|
|
17
|
+
|
|
18
|
+
- **Local-first**: All secrets stored securely on your machine
|
|
19
|
+
- **AES Encryption**: Protects your secrets even if your disk is compromised
|
|
20
|
+
- **Offline Mode**: No internet needed to operate
|
|
21
|
+
- **Stream-Safe**: No .env file leaks on screen while coding or streaming
|
|
22
|
+
- **Simple CLI**: Add, copy, list, and retrieve secrets instantly
|
|
23
|
+
- **Cross-Project Friendly**: Works with React, Node.js, Python, and more
|
|
24
|
+
- **GitHub Action**: Automatically scan commits for exposed secrets ([Learn more](GITHUB_ACTION.md))
|
|
25
|
+
|
|
26
|
+
## 🚀 Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Install from PyPI (recommended)
|
|
32
|
+
pip install envlockr
|
|
33
|
+
|
|
34
|
+
# With clipboard support
|
|
35
|
+
pip install envlockr[clipboard]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
<details>
|
|
39
|
+
<summary>Alternative: Install from source</summary>
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/RohanRatwani/envlockr-cli.git
|
|
43
|
+
cd envlockr-cli
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
</details>
|
|
47
|
+
|
|
48
|
+
### 2. Commands
|
|
49
|
+
|
|
50
|
+
| Command | Example | What it Does |
|
|
51
|
+
|---------|---------|-------------|
|
|
52
|
+
| add | `envlockr add STRIPE_KEY` | Add a new secret |
|
|
53
|
+
| get | `envlockr get STRIPE_KEY` | Retrieve a secret |
|
|
54
|
+
| list | `envlockr list` | List all stored secrets |
|
|
55
|
+
| copy | `envlockr copy STRIPE_KEY` | Copy secret to clipboard |
|
|
56
|
+
| update | `envlockr update STRIPE_KEY` | Update an existing secret |
|
|
57
|
+
| delete | `envlockr delete STRIPE_KEY` | Delete a secret |
|
|
58
|
+
| export | `envlockr export --output .env` | Export all secrets to .env file |
|
|
59
|
+
| import | `envlockr import .env` | Import secrets from .env file |
|
|
60
|
+
| --version | `envlockr --version` | Show version number |
|
|
61
|
+
## ⚡ How to Use in Your Projects
|
|
62
|
+
|
|
63
|
+
### 🖥 Node.js / React / Vite / Next.js
|
|
64
|
+
|
|
65
|
+
#### Option 1: Export to .env file
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
envlockr export --output .env
|
|
69
|
+
npm run dev
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Option 2: Inline Injection (no .env needed)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export STRIPE_KEY=$(envlockr get STRIPE_KEY)
|
|
76
|
+
npm run dev
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Option 3: Import existing .env file
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Migrate your existing .env to encrypted storage
|
|
83
|
+
envlockr import .env
|
|
84
|
+
rm .env # Delete the unencrypted file
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 🛠 Compatible with
|
|
88
|
+
|
|
89
|
+
- create-react-app
|
|
90
|
+
- Next.js
|
|
91
|
+
- Vite
|
|
92
|
+
- Remix
|
|
93
|
+
- Express
|
|
94
|
+
- NestJS
|
|
95
|
+
- and more!
|
|
96
|
+
|
|
97
|
+
👉 **[See framework-specific examples](EXAMPLES.md)** - React, Next.js, Python, Docker, and more!
|
|
98
|
+
|
|
99
|
+
## 📦 Local Storage
|
|
100
|
+
|
|
101
|
+
All your secrets are encrypted and stored in:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
~/.envlockr/vault.json
|
|
105
|
+
~/.envlockr/key.key
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- ✅ AES-256 level security
|
|
109
|
+
- ✅ No external cloud or server dependency
|
|
110
|
+
|
|
111
|
+
## � GitHub Action - Prevent Secret Leaks
|
|
112
|
+
|
|
113
|
+
EnvLockr includes a **GitHub Action** that automatically scans your repository for exposed secrets!
|
|
114
|
+
|
|
115
|
+
### Quick Setup
|
|
116
|
+
|
|
117
|
+
Add this to `.github/workflows/envlockr-scan.yml`:
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
name: EnvLockr Secret Scan
|
|
121
|
+
on: [pull_request, push]
|
|
122
|
+
|
|
123
|
+
jobs:
|
|
124
|
+
scan:
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
steps:
|
|
127
|
+
- uses: actions/checkout@v4
|
|
128
|
+
- name: Scan for secrets
|
|
129
|
+
run: |
|
|
130
|
+
curl -sSL https://raw.githubusercontent.com/RohanRatwani/envlockr-cli/main/.github/scripts/scan-secrets.sh | bash
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**What it does:**
|
|
134
|
+
- ✅ Scans commits for API keys, tokens, and passwords
|
|
135
|
+
- ✅ Blocks PRs if secrets are detected
|
|
136
|
+
- ✅ Posts helpful comments with fix instructions
|
|
137
|
+
- ✅ Detects AWS, Stripe, GitHub, Google, OpenAI, and more
|
|
138
|
+
|
|
139
|
+
👉 [Full GitHub Action Documentation](GITHUB_ACTION.md)
|
|
140
|
+
|
|
141
|
+
## Why EnvLockr?
|
|
142
|
+
|
|
143
|
+
- 🔒 **Protect your keys** without trusting the cloud
|
|
144
|
+
- 🎥 **Stream coding sessions** without leaking environment secrets
|
|
145
|
+
- ⚡ **Speed up local development** with simple, fast secret management
|
|
146
|
+
|
|
147
|
+
## 📚 Documentation
|
|
148
|
+
|
|
149
|
+
- **[Quick Reference Guide](QUICKSTART.md)** - Common commands and workflows
|
|
150
|
+
- **[Framework Examples](EXAMPLES.md)** - React, Next.js, Python, Node.js, Docker
|
|
151
|
+
- **[GitHub Action Setup](GITHUB_ACTION.md)** - Prevent secret leaks in CI/CD
|
|
152
|
+
- **[Contributing Guide](CONTRIBUTING.md)** - Help improve EnvLockr
|
|
153
|
+
- **[Changelog](CHANGELOG.md)** - Version history
|
|
154
|
+
|
|
155
|
+
## ☕ Support the Project
|
|
156
|
+
|
|
157
|
+
If EnvLockr helps you, consider supporting its development:
|
|
158
|
+
|
|
159
|
+
<a href="https://www.buymeacoffee.com/rohanratwani" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 40px !important;width: 145px !important;" ></a>
|
|
160
|
+
|
|
161
|
+
Or [become a GitHub Sponsor](https://github.com/sponsors/RohanRatwani)
|
|
162
|
+
|
|
163
|
+
## 📥 What's Next?
|
|
164
|
+
|
|
165
|
+
We're exploring new features for EnvLockr, including:
|
|
166
|
+
- Advanced environment injection
|
|
167
|
+
- Project-specific vaults
|
|
168
|
+
- Desktop UI app
|
|
169
|
+
- IDE integrations (VS Code, JetBrains)
|
|
170
|
+
- Team collaboration features
|
|
171
|
+
|
|
172
|
+
👉 [Join the Waitlist](https://envlockr.netlify.app/) and share your thoughts!
|
|
173
|
+
|
|
174
|
+
## 💡 We Want Your Feedback!
|
|
175
|
+
|
|
176
|
+
Which features would you love to see in EnvLockr?
|
|
177
|
+
- 💬 [GitHub Discussions](https://github.com/RohanRatwani/envlockr-cli/discussions)
|
|
178
|
+
- 🐛 [Report Issues](https://github.com/RohanRatwani/envlockr-cli/issues)
|
|
179
|
+
- 🌟 [Star us on GitHub](https://github.com/RohanRatwani/envlockr-cli)
|
|
180
|
+
|
|
181
|
+
## 📚 Documentation
|
|
182
|
+
|
|
183
|
+
- [Quick Reference Guide](QUICKSTART.md) - Common commands and workflows
|
|
184
|
+
- [GitHub Action Setup](GITHUB_ACTION.md) - Prevent secret leaks in CI/CD
|
|
185
|
+
- [Contributing Guide](CONTRIBUTING.md) - Help improve EnvLockr
|
|
186
|
+
- [Changelog](CHANGELOG.md) - Version history
|
|
187
|
+
|
|
188
|
+
## 🛡 License
|
|
189
|
+
|
|
190
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
<p align="center">
|
|
195
|
+
Made with ❤️ by <a href="https://github.com/RohanRatwani">Rohan Ratwani</a>
|
|
196
|
+
<br>
|
|
197
|
+
<a href="https://envlockr.netlify.app">Website</a> ·
|
|
198
|
+
<a href="https://github.com/RohanRatwani/envlockr-cli">GitHub</a> ·
|
|
199
|
+
<a href="https://twitter.com/envlockr">Twitter</a>
|
|
200
|
+
</p>
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: envlockr
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Secure your environment variables — locally, encrypted, and stream-safe.
|
|
5
|
+
Author-email: Rohan Ratwani <rohanratwani@example.com>
|
|
6
|
+
Maintainer-email: Rohan Ratwani <rohanratwani@example.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://envlockr.netlify.app
|
|
9
|
+
Project-URL: Documentation, https://github.com/RohanRatwani/envlockr-cli#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/RohanRatwani/envlockr-cli
|
|
11
|
+
Project-URL: Issues, https://github.com/RohanRatwani/envlockr-cli/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/RohanRatwani/envlockr-cli/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: environment-variables,secrets,encryption,security,cli,env,dotenv,secrets-management,local-first,developer-tools
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Topic :: Security
|
|
27
|
+
Classifier: Topic :: Security :: Cryptography
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
+
Classifier: Topic :: Utilities
|
|
30
|
+
Requires-Python: >=3.7
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: cryptography>=3.4.0
|
|
34
|
+
Provides-Extra: clipboard
|
|
35
|
+
Requires-Dist: pyperclip>=1.8.0; extra == "clipboard"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: flake8>=4.0.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<img src="banner.png" alt="EnvLockr - Secure Your Environment Variables Locally" />
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
# 🔐 EnvLockr CLI
|
|
47
|
+
|
|
48
|
+
[](https://pypi.org/project/envlockr/)
|
|
49
|
+
[](https://opensource.org/licenses/MIT)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](https://github.com/RohanRatwani/envlockr-cli/stargazers)
|
|
52
|
+
|
|
53
|
+
> Secure your environment variables — locally, encrypted, and stream-safe.
|
|
54
|
+
|
|
55
|
+
EnvLockr CLI is a tool for developers, streamers, and indie hackers who want full control of their secrets without relying on cloud services.
|
|
56
|
+
|
|
57
|
+
## ✨ Features
|
|
58
|
+
|
|
59
|
+
- **Local-first**: All secrets stored securely on your machine
|
|
60
|
+
- **AES Encryption**: Protects your secrets even if your disk is compromised
|
|
61
|
+
- **Offline Mode**: No internet needed to operate
|
|
62
|
+
- **Stream-Safe**: No .env file leaks on screen while coding or streaming
|
|
63
|
+
- **Simple CLI**: Add, copy, list, and retrieve secrets instantly
|
|
64
|
+
- **Cross-Project Friendly**: Works with React, Node.js, Python, and more
|
|
65
|
+
- **GitHub Action**: Automatically scan commits for exposed secrets ([Learn more](GITHUB_ACTION.md))
|
|
66
|
+
|
|
67
|
+
## 🚀 Quick Start
|
|
68
|
+
|
|
69
|
+
### 1. Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Install from PyPI (recommended)
|
|
73
|
+
pip install envlockr
|
|
74
|
+
|
|
75
|
+
# With clipboard support
|
|
76
|
+
pip install envlockr[clipboard]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
<details>
|
|
80
|
+
<summary>Alternative: Install from source</summary>
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/RohanRatwani/envlockr-cli.git
|
|
84
|
+
cd envlockr-cli
|
|
85
|
+
pip install -e .
|
|
86
|
+
```
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
### 2. Commands
|
|
90
|
+
|
|
91
|
+
| Command | Example | What it Does |
|
|
92
|
+
|---------|---------|-------------|
|
|
93
|
+
| add | `envlockr add STRIPE_KEY` | Add a new secret |
|
|
94
|
+
| get | `envlockr get STRIPE_KEY` | Retrieve a secret |
|
|
95
|
+
| list | `envlockr list` | List all stored secrets |
|
|
96
|
+
| copy | `envlockr copy STRIPE_KEY` | Copy secret to clipboard |
|
|
97
|
+
| update | `envlockr update STRIPE_KEY` | Update an existing secret |
|
|
98
|
+
| delete | `envlockr delete STRIPE_KEY` | Delete a secret |
|
|
99
|
+
| export | `envlockr export --output .env` | Export all secrets to .env file |
|
|
100
|
+
| import | `envlockr import .env` | Import secrets from .env file |
|
|
101
|
+
| --version | `envlockr --version` | Show version number |
|
|
102
|
+
## ⚡ How to Use in Your Projects
|
|
103
|
+
|
|
104
|
+
### 🖥 Node.js / React / Vite / Next.js
|
|
105
|
+
|
|
106
|
+
#### Option 1: Export to .env file
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
envlockr export --output .env
|
|
110
|
+
npm run dev
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Option 2: Inline Injection (no .env needed)
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
export STRIPE_KEY=$(envlockr get STRIPE_KEY)
|
|
117
|
+
npm run dev
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Option 3: Import existing .env file
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Migrate your existing .env to encrypted storage
|
|
124
|
+
envlockr import .env
|
|
125
|
+
rm .env # Delete the unencrypted file
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 🛠 Compatible with
|
|
129
|
+
|
|
130
|
+
- create-react-app
|
|
131
|
+
- Next.js
|
|
132
|
+
- Vite
|
|
133
|
+
- Remix
|
|
134
|
+
- Express
|
|
135
|
+
- NestJS
|
|
136
|
+
- and more!
|
|
137
|
+
|
|
138
|
+
👉 **[See framework-specific examples](EXAMPLES.md)** - React, Next.js, Python, Docker, and more!
|
|
139
|
+
|
|
140
|
+
## 📦 Local Storage
|
|
141
|
+
|
|
142
|
+
All your secrets are encrypted and stored in:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
~/.envlockr/vault.json
|
|
146
|
+
~/.envlockr/key.key
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- ✅ AES-256 level security
|
|
150
|
+
- ✅ No external cloud or server dependency
|
|
151
|
+
|
|
152
|
+
## � GitHub Action - Prevent Secret Leaks
|
|
153
|
+
|
|
154
|
+
EnvLockr includes a **GitHub Action** that automatically scans your repository for exposed secrets!
|
|
155
|
+
|
|
156
|
+
### Quick Setup
|
|
157
|
+
|
|
158
|
+
Add this to `.github/workflows/envlockr-scan.yml`:
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
name: EnvLockr Secret Scan
|
|
162
|
+
on: [pull_request, push]
|
|
163
|
+
|
|
164
|
+
jobs:
|
|
165
|
+
scan:
|
|
166
|
+
runs-on: ubuntu-latest
|
|
167
|
+
steps:
|
|
168
|
+
- uses: actions/checkout@v4
|
|
169
|
+
- name: Scan for secrets
|
|
170
|
+
run: |
|
|
171
|
+
curl -sSL https://raw.githubusercontent.com/RohanRatwani/envlockr-cli/main/.github/scripts/scan-secrets.sh | bash
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**What it does:**
|
|
175
|
+
- ✅ Scans commits for API keys, tokens, and passwords
|
|
176
|
+
- ✅ Blocks PRs if secrets are detected
|
|
177
|
+
- ✅ Posts helpful comments with fix instructions
|
|
178
|
+
- ✅ Detects AWS, Stripe, GitHub, Google, OpenAI, and more
|
|
179
|
+
|
|
180
|
+
👉 [Full GitHub Action Documentation](GITHUB_ACTION.md)
|
|
181
|
+
|
|
182
|
+
## Why EnvLockr?
|
|
183
|
+
|
|
184
|
+
- 🔒 **Protect your keys** without trusting the cloud
|
|
185
|
+
- 🎥 **Stream coding sessions** without leaking environment secrets
|
|
186
|
+
- ⚡ **Speed up local development** with simple, fast secret management
|
|
187
|
+
|
|
188
|
+
## 📚 Documentation
|
|
189
|
+
|
|
190
|
+
- **[Quick Reference Guide](QUICKSTART.md)** - Common commands and workflows
|
|
191
|
+
- **[Framework Examples](EXAMPLES.md)** - React, Next.js, Python, Node.js, Docker
|
|
192
|
+
- **[GitHub Action Setup](GITHUB_ACTION.md)** - Prevent secret leaks in CI/CD
|
|
193
|
+
- **[Contributing Guide](CONTRIBUTING.md)** - Help improve EnvLockr
|
|
194
|
+
- **[Changelog](CHANGELOG.md)** - Version history
|
|
195
|
+
|
|
196
|
+
## ☕ Support the Project
|
|
197
|
+
|
|
198
|
+
If EnvLockr helps you, consider supporting its development:
|
|
199
|
+
|
|
200
|
+
<a href="https://www.buymeacoffee.com/rohanratwani" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 40px !important;width: 145px !important;" ></a>
|
|
201
|
+
|
|
202
|
+
Or [become a GitHub Sponsor](https://github.com/sponsors/RohanRatwani)
|
|
203
|
+
|
|
204
|
+
## 📥 What's Next?
|
|
205
|
+
|
|
206
|
+
We're exploring new features for EnvLockr, including:
|
|
207
|
+
- Advanced environment injection
|
|
208
|
+
- Project-specific vaults
|
|
209
|
+
- Desktop UI app
|
|
210
|
+
- IDE integrations (VS Code, JetBrains)
|
|
211
|
+
- Team collaboration features
|
|
212
|
+
|
|
213
|
+
👉 [Join the Waitlist](https://envlockr.netlify.app/) and share your thoughts!
|
|
214
|
+
|
|
215
|
+
## 💡 We Want Your Feedback!
|
|
216
|
+
|
|
217
|
+
Which features would you love to see in EnvLockr?
|
|
218
|
+
- 💬 [GitHub Discussions](https://github.com/RohanRatwani/envlockr-cli/discussions)
|
|
219
|
+
- 🐛 [Report Issues](https://github.com/RohanRatwani/envlockr-cli/issues)
|
|
220
|
+
- 🌟 [Star us on GitHub](https://github.com/RohanRatwani/envlockr-cli)
|
|
221
|
+
|
|
222
|
+
## 📚 Documentation
|
|
223
|
+
|
|
224
|
+
- [Quick Reference Guide](QUICKSTART.md) - Common commands and workflows
|
|
225
|
+
- [GitHub Action Setup](GITHUB_ACTION.md) - Prevent secret leaks in CI/CD
|
|
226
|
+
- [Contributing Guide](CONTRIBUTING.md) - Help improve EnvLockr
|
|
227
|
+
- [Changelog](CHANGELOG.md) - Version history
|
|
228
|
+
|
|
229
|
+
## 🛡 License
|
|
230
|
+
|
|
231
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
<p align="center">
|
|
236
|
+
Made with ❤️ by <a href="https://github.com/RohanRatwani">Rohan Ratwani</a>
|
|
237
|
+
<br>
|
|
238
|
+
<a href="https://envlockr.netlify.app">Website</a> ·
|
|
239
|
+
<a href="https://github.com/RohanRatwani/envlockr-cli">GitHub</a> ·
|
|
240
|
+
<a href="https://twitter.com/envlockr">Twitter</a>
|
|
241
|
+
</p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
envlockr
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import getpass
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import stat
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
# Version
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
|
+
|
|
11
|
+
# Try to import optional dependencies
|
|
12
|
+
pyperclip = None # type: ignore
|
|
13
|
+
try:
|
|
14
|
+
import pyperclip
|
|
15
|
+
PYPERCLIP_AVAILABLE = True
|
|
16
|
+
except ImportError:
|
|
17
|
+
PYPERCLIP_AVAILABLE = False
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from cryptography.fernet import Fernet, InvalidToken
|
|
21
|
+
except ImportError:
|
|
22
|
+
print("❌ Error: 'cryptography' package is required.")
|
|
23
|
+
print(" Install it with: pip install cryptography")
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
# Constants
|
|
27
|
+
VAULT_DIR = os.path.expanduser("~/.envlockr")
|
|
28
|
+
VAULT_FILE = os.path.join(VAULT_DIR, "vault.json")
|
|
29
|
+
KEY_FILE = os.path.join(VAULT_DIR, "key.key")
|
|
30
|
+
|
|
31
|
+
# Color support
|
|
32
|
+
class Colors:
|
|
33
|
+
"""ANSI color codes for terminal output"""
|
|
34
|
+
RED = '\033[0;31m'
|
|
35
|
+
GREEN = '\033[0;32m'
|
|
36
|
+
YELLOW = '\033[1;33m'
|
|
37
|
+
BLUE = '\033[0;34m'
|
|
38
|
+
CYAN = '\033[0;36m'
|
|
39
|
+
BOLD = '\033[1m'
|
|
40
|
+
NC = '\033[0m' # No Color
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def disable(cls):
|
|
44
|
+
"""Disable colors (for non-TTY or Windows without color support)"""
|
|
45
|
+
cls.RED = cls.GREEN = cls.YELLOW = cls.BLUE = cls.CYAN = cls.BOLD = cls.NC = ''
|
|
46
|
+
|
|
47
|
+
# Disable colors if not a TTY or on Windows without proper support
|
|
48
|
+
if not sys.stdout.isatty():
|
|
49
|
+
Colors.disable()
|
|
50
|
+
elif sys.platform == 'win32':
|
|
51
|
+
try:
|
|
52
|
+
# Enable ANSI colors on Windows 10+
|
|
53
|
+
import ctypes
|
|
54
|
+
kernel32 = ctypes.windll.kernel32
|
|
55
|
+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
|
|
56
|
+
except Exception:
|
|
57
|
+
Colors.disable()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def print_success(message):
|
|
61
|
+
"""Print success message in green"""
|
|
62
|
+
print(f"{Colors.GREEN}✅ {message}{Colors.NC}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def print_error(message):
|
|
66
|
+
"""Print error message in red"""
|
|
67
|
+
print(f"{Colors.RED}❌ {message}{Colors.NC}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def print_warning(message):
|
|
71
|
+
"""Print warning message in yellow"""
|
|
72
|
+
print(f"{Colors.YELLOW}⚠️ {message}{Colors.NC}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def print_info(message):
|
|
76
|
+
"""Print info message in blue"""
|
|
77
|
+
print(f"{Colors.BLUE}ℹ️ {message}{Colors.NC}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Ensure the ~/.envlockr directory exists
|
|
81
|
+
def ensure_vault_dir():
|
|
82
|
+
"""Create vault directory with secure permissions"""
|
|
83
|
+
if not os.path.exists(VAULT_DIR):
|
|
84
|
+
os.makedirs(VAULT_DIR, mode=0o700)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def check_key_file_security():
|
|
88
|
+
"""Warn if key file has insecure permissions (Unix-like systems)"""
|
|
89
|
+
if sys.platform == 'win32':
|
|
90
|
+
return # Windows handles permissions differently
|
|
91
|
+
|
|
92
|
+
if os.path.exists(KEY_FILE):
|
|
93
|
+
try:
|
|
94
|
+
file_stat = os.stat(KEY_FILE)
|
|
95
|
+
mode = file_stat.st_mode
|
|
96
|
+
# Check if group or others have any permissions
|
|
97
|
+
if mode & (stat.S_IRWXG | stat.S_IRWXO):
|
|
98
|
+
print_warning(f"Key file '{KEY_FILE}' has insecure permissions!")
|
|
99
|
+
print_warning("Run: chmod 600 ~/.envlockr/key.key")
|
|
100
|
+
except OSError:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Utilities
|
|
105
|
+
def load_or_create_key():
|
|
106
|
+
"""Load existing key or create a new one"""
|
|
107
|
+
ensure_vault_dir()
|
|
108
|
+
|
|
109
|
+
if not os.path.exists(KEY_FILE):
|
|
110
|
+
key = Fernet.generate_key()
|
|
111
|
+
# Create key file with secure permissions
|
|
112
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
|
113
|
+
if sys.platform != 'win32':
|
|
114
|
+
fd = os.open(KEY_FILE, flags, 0o600)
|
|
115
|
+
with os.fdopen(fd, 'wb') as f:
|
|
116
|
+
f.write(key)
|
|
117
|
+
else:
|
|
118
|
+
with open(KEY_FILE, 'wb') as f:
|
|
119
|
+
f.write(key)
|
|
120
|
+
else:
|
|
121
|
+
try:
|
|
122
|
+
with open(KEY_FILE, 'rb') as f:
|
|
123
|
+
key = f.read()
|
|
124
|
+
except PermissionError:
|
|
125
|
+
print_error(f"Permission denied reading key file: {KEY_FILE}")
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
except IOError as e:
|
|
128
|
+
print_error(f"Error reading key file: {e}")
|
|
129
|
+
sys.exit(1)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
return Fernet(key)
|
|
133
|
+
except Exception:
|
|
134
|
+
print_error("Invalid or corrupted key file.")
|
|
135
|
+
print_info("You may need to delete ~/.envlockr/key.key and start fresh.")
|
|
136
|
+
print_warning("WARNING: This will make existing secrets unrecoverable!")
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
|
|
139
|
+
def load_vault():
|
|
140
|
+
"""Load the encrypted vault from disk"""
|
|
141
|
+
ensure_vault_dir()
|
|
142
|
+
|
|
143
|
+
if not os.path.exists(VAULT_FILE):
|
|
144
|
+
return {}
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
with open(VAULT_FILE, 'r') as f:
|
|
148
|
+
return json.load(f)
|
|
149
|
+
except json.JSONDecodeError:
|
|
150
|
+
print_error("Vault file is corrupted.")
|
|
151
|
+
print_info("You may need to delete ~/.envlockr/vault.json and start fresh.")
|
|
152
|
+
sys.exit(1)
|
|
153
|
+
except PermissionError:
|
|
154
|
+
print_error(f"Permission denied reading vault: {VAULT_FILE}")
|
|
155
|
+
sys.exit(1)
|
|
156
|
+
except IOError as e:
|
|
157
|
+
print_error(f"Error reading vault: {e}")
|
|
158
|
+
sys.exit(1)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def save_vault(vault):
|
|
162
|
+
"""Save the vault to disk"""
|
|
163
|
+
ensure_vault_dir()
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
with open(VAULT_FILE, 'w') as f:
|
|
167
|
+
json.dump(vault, f, indent=4)
|
|
168
|
+
except PermissionError:
|
|
169
|
+
print_error(f"Permission denied writing to vault: {VAULT_FILE}")
|
|
170
|
+
sys.exit(1)
|
|
171
|
+
except IOError as e:
|
|
172
|
+
print_error(f"Error saving vault: {e}")
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def decrypt_secret(fernet, encrypted_value):
|
|
177
|
+
"""Safely decrypt a secret value"""
|
|
178
|
+
try:
|
|
179
|
+
return fernet.decrypt(encrypted_value.encode()).decode()
|
|
180
|
+
except InvalidToken:
|
|
181
|
+
print_error("Failed to decrypt secret. Key may have changed.")
|
|
182
|
+
print_info("If you regenerated your key, existing secrets cannot be recovered.")
|
|
183
|
+
return None
|
|
184
|
+
except Exception as e:
|
|
185
|
+
print_error(f"Decryption error: {e}")
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# CLI Commands
|
|
190
|
+
def add_secret(args):
|
|
191
|
+
"""Add a new secret to the vault"""
|
|
192
|
+
fernet = load_or_create_key()
|
|
193
|
+
vault = load_vault()
|
|
194
|
+
|
|
195
|
+
if args.name in vault and not getattr(args, 'force', False):
|
|
196
|
+
print_warning(f"Secret '{args.name}' already exists.")
|
|
197
|
+
response = input("Overwrite? [y/N]: ").strip().lower()
|
|
198
|
+
if response != 'y':
|
|
199
|
+
print_info("Operation cancelled.")
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
secret = getpass.getpass(prompt="Enter secret value: ")
|
|
203
|
+
if not secret:
|
|
204
|
+
print_error("Secret value cannot be empty.")
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
encrypted = fernet.encrypt(secret.encode()).decode()
|
|
208
|
+
vault[args.name] = encrypted
|
|
209
|
+
save_vault(vault)
|
|
210
|
+
print_success(f"Secret '{args.name}' added successfully.")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_secret(args):
|
|
214
|
+
"""Retrieve and display a secret"""
|
|
215
|
+
fernet = load_or_create_key()
|
|
216
|
+
vault = load_vault()
|
|
217
|
+
|
|
218
|
+
if args.name not in vault:
|
|
219
|
+
print_error(f"Secret '{args.name}' not found.")
|
|
220
|
+
print_info("Use 'envlockr list' to see available secrets.")
|
|
221
|
+
return
|
|
222
|
+
|
|
223
|
+
decrypted = decrypt_secret(fernet, vault[args.name])
|
|
224
|
+
if decrypted is not None:
|
|
225
|
+
print(decrypted)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def list_secrets(args):
|
|
229
|
+
"""List all stored secret names"""
|
|
230
|
+
vault = load_vault()
|
|
231
|
+
|
|
232
|
+
if not vault:
|
|
233
|
+
print_info("No secrets stored yet.")
|
|
234
|
+
print_info("Add your first secret: envlockr add MY_SECRET")
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
print(f"{Colors.CYAN}🔐 Stored Secrets ({len(vault)}){Colors.NC}")
|
|
238
|
+
for name in sorted(vault.keys()):
|
|
239
|
+
print(f" {Colors.BOLD}•{Colors.NC} {name}")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def copy_secret(args):
|
|
243
|
+
"""Copy a secret to clipboard"""
|
|
244
|
+
if not PYPERCLIP_AVAILABLE:
|
|
245
|
+
print_error("Clipboard support not available.")
|
|
246
|
+
print_info("Install pyperclip: pip install pyperclip")
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
fernet = load_or_create_key()
|
|
250
|
+
vault = load_vault()
|
|
251
|
+
|
|
252
|
+
if args.name not in vault:
|
|
253
|
+
print_error(f"Secret '{args.name}' not found.")
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
decrypted = decrypt_secret(fernet, vault[args.name])
|
|
257
|
+
if decrypted is not None:
|
|
258
|
+
try:
|
|
259
|
+
pyperclip.copy(decrypted) # type: ignore[union-attr]
|
|
260
|
+
print_success(f"Secret '{args.name}' copied to clipboard.")
|
|
261
|
+
except Exception as e:
|
|
262
|
+
print_error(f"Failed to copy to clipboard: {e}")
|
|
263
|
+
print_info("Use 'envlockr get' to display the secret instead.")
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def delete_secret(args):
|
|
267
|
+
"""Delete a secret from the vault"""
|
|
268
|
+
vault = load_vault()
|
|
269
|
+
|
|
270
|
+
if args.name not in vault:
|
|
271
|
+
print_error(f"Secret '{args.name}' not found.")
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
if not getattr(args, 'force', False):
|
|
275
|
+
response = input(f"Delete secret '{args.name}'? [y/N]: ").strip().lower()
|
|
276
|
+
if response != 'y':
|
|
277
|
+
print_info("Operation cancelled.")
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
del vault[args.name]
|
|
281
|
+
save_vault(vault)
|
|
282
|
+
print_success(f"Secret '{args.name}' deleted.")
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def update_secret(args):
|
|
286
|
+
"""Update an existing secret"""
|
|
287
|
+
fernet = load_or_create_key()
|
|
288
|
+
vault = load_vault()
|
|
289
|
+
|
|
290
|
+
if args.name not in vault:
|
|
291
|
+
print_error(f"Secret '{args.name}' not found.")
|
|
292
|
+
print_info("Use 'envlockr add' to create a new secret.")
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
secret = getpass.getpass(prompt="Enter new secret value: ")
|
|
296
|
+
if not secret:
|
|
297
|
+
print_error("Secret value cannot be empty.")
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
encrypted = fernet.encrypt(secret.encode()).decode()
|
|
301
|
+
vault[args.name] = encrypted
|
|
302
|
+
save_vault(vault)
|
|
303
|
+
print_success(f"Secret '{args.name}' updated.")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def export_secrets(args):
|
|
307
|
+
"""Export all secrets to a .env file"""
|
|
308
|
+
fernet = load_or_create_key()
|
|
309
|
+
vault = load_vault()
|
|
310
|
+
|
|
311
|
+
if not vault:
|
|
312
|
+
print_info("No secrets to export.")
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
output_file = args.output if args.output else ".env"
|
|
316
|
+
|
|
317
|
+
# Check if file exists
|
|
318
|
+
if os.path.exists(output_file) and not getattr(args, 'force', False):
|
|
319
|
+
print_warning(f"File '{output_file}' already exists.")
|
|
320
|
+
response = input("Overwrite? [y/N]: ").strip().lower()
|
|
321
|
+
if response != 'y':
|
|
322
|
+
print_info("Operation cancelled.")
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
exported_count = 0
|
|
327
|
+
with open(output_file, 'w') as f:
|
|
328
|
+
f.write(f"# Generated by EnvLockr v{__version__}\n")
|
|
329
|
+
f.write(f"# {len(vault)} secrets exported\n\n")
|
|
330
|
+
for name, encrypted_value in sorted(vault.items()):
|
|
331
|
+
decrypted = decrypt_secret(fernet, encrypted_value)
|
|
332
|
+
if decrypted is not None:
|
|
333
|
+
# Handle multi-line values and special characters
|
|
334
|
+
if '\n' in decrypted or '"' in decrypted:
|
|
335
|
+
f.write(f'{name}="{decrypted}"\n')
|
|
336
|
+
else:
|
|
337
|
+
f.write(f"{name}={decrypted}\n")
|
|
338
|
+
exported_count += 1
|
|
339
|
+
|
|
340
|
+
print_success(f"Exported {exported_count} secrets to '{output_file}'")
|
|
341
|
+
print_warning(f"Remember: Add '{output_file}' to .gitignore!")
|
|
342
|
+
except PermissionError:
|
|
343
|
+
print_error(f"Permission denied writing to '{output_file}'")
|
|
344
|
+
except IOError as e:
|
|
345
|
+
print_error(f"Error exporting secrets: {e}")
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def import_secrets(args):
|
|
349
|
+
"""Import secrets from a .env file"""
|
|
350
|
+
fernet = load_or_create_key()
|
|
351
|
+
vault = load_vault()
|
|
352
|
+
|
|
353
|
+
input_file = args.file
|
|
354
|
+
|
|
355
|
+
if not os.path.exists(input_file):
|
|
356
|
+
print_error(f"File '{input_file}' not found.")
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
imported = 0
|
|
361
|
+
skipped = 0
|
|
362
|
+
|
|
363
|
+
with open(input_file, 'r') as f:
|
|
364
|
+
for line_num, line in enumerate(f, 1):
|
|
365
|
+
line = line.strip()
|
|
366
|
+
|
|
367
|
+
# Skip empty lines and comments
|
|
368
|
+
if not line or line.startswith('#'):
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
# Parse KEY=value format
|
|
372
|
+
if '=' not in line:
|
|
373
|
+
print_warning(f"Line {line_num}: Invalid format, skipping")
|
|
374
|
+
skipped += 1
|
|
375
|
+
continue
|
|
376
|
+
|
|
377
|
+
# Split on first = only (value may contain =)
|
|
378
|
+
key, value = line.split('=', 1)
|
|
379
|
+
key = key.strip()
|
|
380
|
+
value = value.strip()
|
|
381
|
+
|
|
382
|
+
# Remove surrounding quotes if present
|
|
383
|
+
if (value.startswith('"') and value.endswith('"')) or \
|
|
384
|
+
(value.startswith("'") and value.endswith("'")):
|
|
385
|
+
value = value[1:-1]
|
|
386
|
+
|
|
387
|
+
if not key:
|
|
388
|
+
print_warning(f"Line {line_num}: Empty key, skipping")
|
|
389
|
+
skipped += 1
|
|
390
|
+
continue
|
|
391
|
+
|
|
392
|
+
# Check for existing secret
|
|
393
|
+
if key in vault and not getattr(args, 'force', False):
|
|
394
|
+
print_warning(f"Secret '{key}' already exists, skipping (use --force to overwrite)")
|
|
395
|
+
skipped += 1
|
|
396
|
+
continue
|
|
397
|
+
|
|
398
|
+
# Encrypt and store
|
|
399
|
+
encrypted = fernet.encrypt(value.encode()).decode()
|
|
400
|
+
vault[key] = encrypted
|
|
401
|
+
imported += 1
|
|
402
|
+
|
|
403
|
+
save_vault(vault)
|
|
404
|
+
print_success(f"Imported {imported} secrets from '{input_file}'")
|
|
405
|
+
if skipped > 0:
|
|
406
|
+
print_info(f"Skipped {skipped} entries")
|
|
407
|
+
print_warning(f"Consider deleting '{input_file}' now that secrets are secure!")
|
|
408
|
+
|
|
409
|
+
except PermissionError:
|
|
410
|
+
print_error(f"Permission denied reading '{input_file}'")
|
|
411
|
+
except IOError as e:
|
|
412
|
+
print_error(f"Error reading file: {e}")
|
|
413
|
+
|
|
414
|
+
def main():
|
|
415
|
+
"""Main entry point for EnvLockr CLI"""
|
|
416
|
+
parser = argparse.ArgumentParser(
|
|
417
|
+
description="EnvLockr CLI - Secure Local Secrets Manager",
|
|
418
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
419
|
+
epilog="""
|
|
420
|
+
Examples:
|
|
421
|
+
envlockr add API_KEY Add a new secret
|
|
422
|
+
envlockr get API_KEY Retrieve a secret
|
|
423
|
+
envlockr copy API_KEY Copy secret to clipboard
|
|
424
|
+
envlockr list List all secrets
|
|
425
|
+
envlockr export Export to .env file
|
|
426
|
+
envlockr import .env Import from .env file
|
|
427
|
+
|
|
428
|
+
Documentation: https://github.com/RohanRatwani/envlockr-cli
|
|
429
|
+
"""
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
parser.add_argument(
|
|
433
|
+
'--version', '-v',
|
|
434
|
+
action='version',
|
|
435
|
+
version=f'EnvLockr v{__version__}'
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
subparsers = parser.add_subparsers(dest='command', metavar='command')
|
|
439
|
+
|
|
440
|
+
# Add
|
|
441
|
+
add_parser = subparsers.add_parser('add', help='Add a new secret')
|
|
442
|
+
add_parser.add_argument('name', help='Name of the secret (e.g., API_KEY)')
|
|
443
|
+
add_parser.add_argument('--force', '-f', action='store_true', help='Overwrite without confirmation')
|
|
444
|
+
add_parser.set_defaults(func=add_secret)
|
|
445
|
+
|
|
446
|
+
# Get
|
|
447
|
+
get_parser = subparsers.add_parser('get', help='Retrieve a secret (prints to stdout)')
|
|
448
|
+
get_parser.add_argument('name', help='Name of the secret')
|
|
449
|
+
get_parser.set_defaults(func=get_secret)
|
|
450
|
+
|
|
451
|
+
# List
|
|
452
|
+
list_parser = subparsers.add_parser('list', help='List all stored secrets')
|
|
453
|
+
list_parser.set_defaults(func=list_secrets)
|
|
454
|
+
|
|
455
|
+
# Copy
|
|
456
|
+
copy_parser = subparsers.add_parser('copy', help='Copy a secret to clipboard')
|
|
457
|
+
copy_parser.add_argument('name', help='Name of the secret')
|
|
458
|
+
copy_parser.set_defaults(func=copy_secret)
|
|
459
|
+
|
|
460
|
+
# Delete
|
|
461
|
+
delete_parser = subparsers.add_parser('delete', help='Delete a secret')
|
|
462
|
+
delete_parser.add_argument('name', help='Name of the secret')
|
|
463
|
+
delete_parser.add_argument('--force', '-f', action='store_true', help='Delete without confirmation')
|
|
464
|
+
delete_parser.set_defaults(func=delete_secret)
|
|
465
|
+
|
|
466
|
+
# Update
|
|
467
|
+
update_parser = subparsers.add_parser('update', help='Update an existing secret')
|
|
468
|
+
update_parser.add_argument('name', help='Name of the secret')
|
|
469
|
+
update_parser.set_defaults(func=update_secret)
|
|
470
|
+
|
|
471
|
+
# Export
|
|
472
|
+
export_parser = subparsers.add_parser('export', help='Export secrets to .env file')
|
|
473
|
+
export_parser.add_argument('--output', '-o', default='.env', help='Output file path (default: .env)')
|
|
474
|
+
export_parser.add_argument('--force', '-f', action='store_true', help='Overwrite without confirmation')
|
|
475
|
+
export_parser.set_defaults(func=export_secrets)
|
|
476
|
+
|
|
477
|
+
# Import
|
|
478
|
+
import_parser = subparsers.add_parser('import', help='Import secrets from .env file')
|
|
479
|
+
import_parser.add_argument('file', help='Path to .env file to import')
|
|
480
|
+
import_parser.add_argument('--force', '-f', action='store_true', help='Overwrite existing secrets')
|
|
481
|
+
import_parser.set_defaults(func=import_secrets)
|
|
482
|
+
|
|
483
|
+
args = parser.parse_args()
|
|
484
|
+
|
|
485
|
+
# Check key file security on startup
|
|
486
|
+
check_key_file_security()
|
|
487
|
+
|
|
488
|
+
if hasattr(args, 'func'):
|
|
489
|
+
try:
|
|
490
|
+
args.func(args)
|
|
491
|
+
except KeyboardInterrupt:
|
|
492
|
+
print("\n")
|
|
493
|
+
print_info("Operation cancelled.")
|
|
494
|
+
sys.exit(0)
|
|
495
|
+
except Exception as e:
|
|
496
|
+
print_error(f"Unexpected error: {e}")
|
|
497
|
+
print_info("Please report this issue at: https://github.com/RohanRatwani/envlockr-cli/issues")
|
|
498
|
+
sys.exit(1)
|
|
499
|
+
else:
|
|
500
|
+
parser.print_help()
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
if __name__ == '__main__':
|
|
504
|
+
main()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "envlockr"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Secure your environment variables — locally, encrypted, and stream-safe."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Rohan Ratwani", email = "rohanratwani@example.com"}
|
|
13
|
+
]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{name = "Rohan Ratwani", email = "rohanratwani@example.com"}
|
|
16
|
+
]
|
|
17
|
+
keywords = [
|
|
18
|
+
"environment-variables",
|
|
19
|
+
"secrets",
|
|
20
|
+
"encryption",
|
|
21
|
+
"security",
|
|
22
|
+
"cli",
|
|
23
|
+
"env",
|
|
24
|
+
"dotenv",
|
|
25
|
+
"secrets-management",
|
|
26
|
+
"local-first",
|
|
27
|
+
"developer-tools"
|
|
28
|
+
]
|
|
29
|
+
classifiers = [
|
|
30
|
+
"Development Status :: 5 - Production/Stable",
|
|
31
|
+
"Environment :: Console",
|
|
32
|
+
"Intended Audience :: Developers",
|
|
33
|
+
"License :: OSI Approved :: MIT License",
|
|
34
|
+
"Operating System :: OS Independent",
|
|
35
|
+
"Programming Language :: Python :: 3",
|
|
36
|
+
"Programming Language :: Python :: 3.7",
|
|
37
|
+
"Programming Language :: Python :: 3.8",
|
|
38
|
+
"Programming Language :: Python :: 3.9",
|
|
39
|
+
"Programming Language :: Python :: 3.10",
|
|
40
|
+
"Programming Language :: Python :: 3.11",
|
|
41
|
+
"Programming Language :: Python :: 3.12",
|
|
42
|
+
"Topic :: Security",
|
|
43
|
+
"Topic :: Security :: Cryptography",
|
|
44
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
45
|
+
"Topic :: Utilities"
|
|
46
|
+
]
|
|
47
|
+
requires-python = ">=3.7"
|
|
48
|
+
dependencies = [
|
|
49
|
+
"cryptography>=3.4.0"
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[project.optional-dependencies]
|
|
53
|
+
clipboard = ["pyperclip>=1.8.0"]
|
|
54
|
+
dev = [
|
|
55
|
+
"pytest>=7.0.0",
|
|
56
|
+
"black>=22.0.0",
|
|
57
|
+
"flake8>=4.0.0"
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[project.urls]
|
|
61
|
+
Homepage = "https://envlockr.netlify.app"
|
|
62
|
+
Documentation = "https://github.com/RohanRatwani/envlockr-cli#readme"
|
|
63
|
+
Repository = "https://github.com/RohanRatwani/envlockr-cli"
|
|
64
|
+
Issues = "https://github.com/RohanRatwani/envlockr-cli/issues"
|
|
65
|
+
Changelog = "https://github.com/RohanRatwani/envlockr-cli/blob/main/CHANGELOG.md"
|
|
66
|
+
|
|
67
|
+
[project.scripts]
|
|
68
|
+
envlockr = "envlockr:main"
|
|
69
|
+
|
|
70
|
+
[tool.setuptools]
|
|
71
|
+
py-modules = ["envlockr"]
|
|
72
|
+
|
|
73
|
+
[tool.setuptools.packages.find]
|
|
74
|
+
where = ["."]
|
|
75
|
+
include = ["envlockr*"]
|
|
76
|
+
|
|
77
|
+
[tool.black]
|
|
78
|
+
line-length = 100
|
|
79
|
+
target-version = ['py37', 'py38', 'py39', 'py310', 'py311']
|
|
80
|
+
|
|
81
|
+
[tool.pytest.ini_options]
|
|
82
|
+
testpaths = ["tests"]
|
|
83
|
+
python_files = ["test_*.py"]
|
envlockr-1.0.0/setup.cfg
ADDED