qwik-envr 0.5.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.
- qwik_envr-0.5.0/.gitignore +16 -0
- qwik_envr-0.5.0/LICENSE +21 -0
- qwik_envr-0.5.0/PKG-INFO +215 -0
- qwik_envr-0.5.0/README.md +192 -0
- qwik_envr-0.5.0/SECURITY.md +36 -0
- qwik_envr-0.5.0/pyproject.toml +57 -0
- qwik_envr-0.5.0/src/envr/__init__.py +62 -0
- qwik_envr-0.5.0/src/envr/__main__.py +10 -0
- qwik_envr-0.5.0/src/envr/cli.py +279 -0
- qwik_envr-0.5.0/src/envr/secretblock.py +71 -0
- qwik_envr-0.5.0/src/envr/secretfile.py +343 -0
- qwik_envr-0.5.0/src/envr/shellrc.py +192 -0
- qwik_envr-0.5.0/src/envr/system.py +174 -0
- qwik_envr-0.5.0/src/envr/webview.py +513 -0
- qwik_envr-0.5.0/tests/test_cli.py +138 -0
- qwik_envr-0.5.0/tests/test_secretblock.py +82 -0
- qwik_envr-0.5.0/tests/test_secretfile.py +154 -0
- qwik_envr-0.5.0/tests/test_shellrc.py +91 -0
- qwik_envr-0.5.0/tests/test_system.py +85 -0
- qwik_envr-0.5.0/tests/test_webview.py +165 -0
qwik_envr-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Himakar Reddy
|
|
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.
|
qwik_envr-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: qwik-envr
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A local browser editor for Unix shell environment variables.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Qwik-Tools/envr
|
|
6
|
+
Project-URL: Repository, https://github.com/Qwik-Tools/envr
|
|
7
|
+
Project-URL: Issues, https://github.com/Qwik-Tools/envr/issues
|
|
8
|
+
Author: Himakar Reddy
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,environment,secrets,shell,unix
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Operating System :: POSIX :: BSD
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Topic :: System :: Systems Administration
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# envr
|
|
25
|
+
|
|
26
|
+
Edit shell environment variables in your browser. Run `envr`, and it opens a local
|
|
27
|
+
table for the `export NAME=value` entries in a marked section of your startup file.
|
|
28
|
+
Add, edit, save, or delete entries without dumping the rest of your shell config.
|
|
29
|
+
|
|
30
|
+
**Package:** `qwik-envr` on PyPI · **Command:** `envr` · **Python:** 3.9+
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pipx install qwik-envr
|
|
36
|
+
# or use a virtual environment
|
|
37
|
+
python3 -m venv .venv
|
|
38
|
+
. .venv/bin/activate
|
|
39
|
+
python -m pip install qwik-envr
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The unrelated PyPI package named `envr` is **not** this project. Do not install both
|
|
43
|
+
in the same environment: they may share the `envr` import/command name.
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
Put two **identical** marker lines in your shell config:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
##</env secrets>
|
|
51
|
+
export API_KEY='your-value'
|
|
52
|
+
export DATABASE_URL='postgres://localhost/example'
|
|
53
|
+
##</env secrets>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then run:
|
|
57
|
+
|
|
58
|
+
```console
|
|
59
|
+
$ envr
|
|
60
|
+
envr: macOS 26.3 (arm64) -- OK
|
|
61
|
+
envr: 2 exports from ~/.zshrc at http://127.0.0.1:49152/<random-token>/
|
|
62
|
+
envr: Ctrl-C to stop
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The port and access token are randomly assigned each run. Your default browser
|
|
66
|
+
opens automatically; the server stays running until **Ctrl-C**. Each row has
|
|
67
|
+
**Save** and **Delete** buttons; use **Add** for new variables. Changes save
|
|
68
|
+
immediately. Refresh the page to reload changes made in another editor.
|
|
69
|
+
|
|
70
|
+
If the startup file is missing, an interactive run asks before creating an empty,
|
|
71
|
+
owner-only file (`0600`, subject to umask). Add the markers yourself afterward.
|
|
72
|
+
Noninteractive runs never prompt or create a file. Missing markers are reported,
|
|
73
|
+
not inserted automatically. Existing files are never overwritten just to create
|
|
74
|
+
a new block.
|
|
75
|
+
|
|
76
|
+
## Platform and shell support
|
|
77
|
+
|
|
78
|
+
The platform gate accepts desktop/server Unix-like systems, including macOS,
|
|
79
|
+
Linux, the BSDs, Solaris, and AIX. Local verification is on macOS, including
|
|
80
|
+
sh/bash integration. Other Unix platforms are not independently verified.
|
|
81
|
+
Windows, mobile Python, and browser/WASI runtimes are not supported.
|
|
82
|
+
|
|
83
|
+
`$SHELL` selects the default **file to edit**:
|
|
84
|
+
|
|
85
|
+
| Shell | Default file |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| zsh | `$ZDOTDIR/.zshrc` if set, otherwise `~/.zshrc` |
|
|
88
|
+
| bash | `~/.bashrc` |
|
|
89
|
+
| ksh | `~/.kshrc` |
|
|
90
|
+
| sh, dash, ash | `~/.profile` |
|
|
91
|
+
|
|
92
|
+
If `$SHELL` is unset or unsupported, envr chooses the first existing file among
|
|
93
|
+
`~/.zshrc`, `~/.bashrc`, and `~/.profile`, falling back to `~/.profile`.
|
|
94
|
+
`--file PATH` overrides this selection. Known filenames select their matching
|
|
95
|
+
validation shell; custom filenames use the supported `$SHELL`, or `sh`.
|
|
96
|
+
The selected shell must be installed to save changes.
|
|
97
|
+
|
|
98
|
+
These are editing defaults, not a guarantee of what every shell startup mode
|
|
99
|
+
loads. Bash login shells may use `.bash_profile`; ksh may require `ENV` to point
|
|
100
|
+
to `.kshrc`. Use `--file` for your setup. Fish/csh syntax is not supported.
|
|
101
|
+
|
|
102
|
+
## Commands
|
|
103
|
+
|
|
104
|
+
| Command | Behavior |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `envr` | Open the local editor in your default browser |
|
|
107
|
+
| `envr --file ~/.bash_profile` | Edit a specific file |
|
|
108
|
+
| `envr --no-browser` | Print the URL without opening a browser |
|
|
109
|
+
| `envr --timeout 120` | Stop after 120 seconds; default `0` means until Ctrl-C |
|
|
110
|
+
| `envr --json` | Print one JSON document with platform/config metadata and variable names, **not values**; no prompts/server |
|
|
111
|
+
| `envr -q` | Unix platform gate only; silent, no config file access |
|
|
112
|
+
| `envr --mac -q` | macOS-only gate; silent, no config file access |
|
|
113
|
+
| `envr --version` | Print the version |
|
|
114
|
+
| `python -m envr` | Same command when using the installed interpreter |
|
|
115
|
+
|
|
116
|
+
Exit codes: `0` = supported platform, `1` = unsupported platform (or server startup
|
|
117
|
+
failure), `2` = invalid usage, `130` = interrupted. Config/marker problems are
|
|
118
|
+
reported but do not change the platform verdict. `--mac` exits `1` off macOS.
|
|
119
|
+
|
|
120
|
+
## Editing rules and safe saves
|
|
121
|
+
|
|
122
|
+
- The **first two** marker lines delimit the block. Put them at top level, not
|
|
123
|
+
inside quoted strings, functions, conditionals, or here-documents.
|
|
124
|
+
- Editable entries are single-line literal `export NAME=value` statements.
|
|
125
|
+
Plain, single-quoted, double-quoted, and escaped literal values are supported.
|
|
126
|
+
- Names use `[A-Za-z_][A-Za-z0-9_]*` (at most 128 characters); values are at most
|
|
127
|
+
4096 characters, without newline, NUL, or invalid Unicode. Duplicate names are
|
|
128
|
+
rejected when adding or editing.
|
|
129
|
+
- Comments and blank lines are preserved. If a block contains shell expressions
|
|
130
|
+
such as `$HOME`, command substitution, multiline values, or other statements,
|
|
131
|
+
the **whole block is read-only**. Edit those blocks manually. Nothing is evaluated
|
|
132
|
+
merely to display the table.
|
|
133
|
+
- Edited values are treated as **literal strings**, safely shell-quoted. Untouched
|
|
134
|
+
entries, inline comments, line endings, and content outside the block stay intact.
|
|
135
|
+
- Existing symlinks are followed; the target is updated without replacing the link.
|
|
136
|
+
The target's Unix permission bits are retained. Atomic replacement changes the
|
|
137
|
+
inode; hard-link relationships, ACLs, and extended attributes are not preserved.
|
|
138
|
+
- External changes are detected before replacement and rejected. Refresh before
|
|
139
|
+
retrying. Use one editor tab/session per file; this is not a multiuser editor.
|
|
140
|
+
|
|
141
|
+
On save, envr writes a private temporary candidate in the same directory, runs
|
|
142
|
+
`<shell> -n` against it, and replaces the live file **only if validation succeeds**.
|
|
143
|
+
If validation fails or the shell is unavailable, the original file is unchanged.
|
|
144
|
+
Temporary files are removed afterward.
|
|
145
|
+
|
|
146
|
+
### Sourcing: important limitation
|
|
147
|
+
|
|
148
|
+
After saving, envr executes `. <file>` in a **child shell** with a 15-second timeout.
|
|
149
|
+
Raw shell output is discarded so secrets cannot leak into HTTP errors or terminal
|
|
150
|
+
logs; the page reports whether sourcing completed successfully.
|
|
151
|
+
|
|
152
|
+
**This executes your entire startup file, including commands outside the block.**
|
|
153
|
+
Use envr only with configurations you trust. Syntax validation is not a sandbox.
|
|
154
|
+
Startup guards may skip parts of a file in a noninteractive child shell.
|
|
155
|
+
|
|
156
|
+
A child process cannot change an already-open terminal's environment. To apply
|
|
157
|
+
changes there, run the appropriate command yourself:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
source ~/.zshrc # zsh
|
|
161
|
+
source ~/.bashrc # bash
|
|
162
|
+
. ~/.profile # POSIX sh
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Security
|
|
166
|
+
|
|
167
|
+
The server binds **127.0.0.1 only**, uses an OS-assigned port and a random 192-bit
|
|
168
|
+
token in the URL, and rejects foreign Host/Origin headers. It has no external
|
|
169
|
+
assets, analytics, CORS permissions, or runtime package dependencies. Responses
|
|
170
|
+
use `no-store`, `no-referrer`, `nosniff`, frame denial, and a nonce-based script CSP.
|
|
171
|
+
JSON writes are bounded and validated; displayed values are not inserted as HTML.
|
|
172
|
+
|
|
173
|
+
The local URL grants **read/write access** and sourcing permission: do not share
|
|
174
|
+
it. It can remain in browser history and terminal scrollback. This is a local
|
|
175
|
+
convenience tool, not encrypted secret storage or a sandbox against other processes
|
|
176
|
+
running as your user. Close the server when finished. See [SECURITY.md](SECURITY.md).
|
|
177
|
+
|
|
178
|
+
## Development
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
git clone https://github.com/Qwik-Tools/envr.git
|
|
182
|
+
cd envr
|
|
183
|
+
python3 -m venv .venv
|
|
184
|
+
. .venv/bin/activate
|
|
185
|
+
python -m pip install --upgrade pip
|
|
186
|
+
python -m pip install -e . --group dev
|
|
187
|
+
python -m pytest
|
|
188
|
+
ruff check src tests
|
|
189
|
+
ruff format --check src tests
|
|
190
|
+
mypy src/envr
|
|
191
|
+
python -m build
|
|
192
|
+
python -m twine check dist/*
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Tests use temporary config files; they do not edit or source your real startup file.
|
|
196
|
+
Each test has a timeout to prevent accidental indefinite server runs.
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
src/envr/system.py platform detection
|
|
200
|
+
src/envr/shellrc.py config selection, read/create prompt
|
|
201
|
+
src/envr/secretblock.py marker extraction helper
|
|
202
|
+
src/envr/secretfile.py literal parsing, CRUD, validated writes, child sourcing
|
|
203
|
+
src/envr/webview.py loopback HTTP API and browser table
|
|
204
|
+
src/envr/cli.py command-line entry point
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The distribution is `qwik-envr`; Python imports remain under `envr`. The main
|
|
208
|
+
library interface is `SecretStore.load(path, shell=...)`, then `items()`,
|
|
209
|
+
`add()`, `update()`, `delete()`, `save()`, and `reload()`. `StoreError.code` is
|
|
210
|
+
stable; error details never include values. Store access must be serialized by
|
|
211
|
+
the caller (the HTTP server does this). Maintained by Qwik-Tools / Himakar Reddy.
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# envr
|
|
2
|
+
|
|
3
|
+
Edit shell environment variables in your browser. Run `envr`, and it opens a local
|
|
4
|
+
table for the `export NAME=value` entries in a marked section of your startup file.
|
|
5
|
+
Add, edit, save, or delete entries without dumping the rest of your shell config.
|
|
6
|
+
|
|
7
|
+
**Package:** `qwik-envr` on PyPI · **Command:** `envr` · **Python:** 3.9+
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pipx install qwik-envr
|
|
13
|
+
# or use a virtual environment
|
|
14
|
+
python3 -m venv .venv
|
|
15
|
+
. .venv/bin/activate
|
|
16
|
+
python -m pip install qwik-envr
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The unrelated PyPI package named `envr` is **not** this project. Do not install both
|
|
20
|
+
in the same environment: they may share the `envr` import/command name.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
Put two **identical** marker lines in your shell config:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
##</env secrets>
|
|
28
|
+
export API_KEY='your-value'
|
|
29
|
+
export DATABASE_URL='postgres://localhost/example'
|
|
30
|
+
##</env secrets>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then run:
|
|
34
|
+
|
|
35
|
+
```console
|
|
36
|
+
$ envr
|
|
37
|
+
envr: macOS 26.3 (arm64) -- OK
|
|
38
|
+
envr: 2 exports from ~/.zshrc at http://127.0.0.1:49152/<random-token>/
|
|
39
|
+
envr: Ctrl-C to stop
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The port and access token are randomly assigned each run. Your default browser
|
|
43
|
+
opens automatically; the server stays running until **Ctrl-C**. Each row has
|
|
44
|
+
**Save** and **Delete** buttons; use **Add** for new variables. Changes save
|
|
45
|
+
immediately. Refresh the page to reload changes made in another editor.
|
|
46
|
+
|
|
47
|
+
If the startup file is missing, an interactive run asks before creating an empty,
|
|
48
|
+
owner-only file (`0600`, subject to umask). Add the markers yourself afterward.
|
|
49
|
+
Noninteractive runs never prompt or create a file. Missing markers are reported,
|
|
50
|
+
not inserted automatically. Existing files are never overwritten just to create
|
|
51
|
+
a new block.
|
|
52
|
+
|
|
53
|
+
## Platform and shell support
|
|
54
|
+
|
|
55
|
+
The platform gate accepts desktop/server Unix-like systems, including macOS,
|
|
56
|
+
Linux, the BSDs, Solaris, and AIX. Local verification is on macOS, including
|
|
57
|
+
sh/bash integration. Other Unix platforms are not independently verified.
|
|
58
|
+
Windows, mobile Python, and browser/WASI runtimes are not supported.
|
|
59
|
+
|
|
60
|
+
`$SHELL` selects the default **file to edit**:
|
|
61
|
+
|
|
62
|
+
| Shell | Default file |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| zsh | `$ZDOTDIR/.zshrc` if set, otherwise `~/.zshrc` |
|
|
65
|
+
| bash | `~/.bashrc` |
|
|
66
|
+
| ksh | `~/.kshrc` |
|
|
67
|
+
| sh, dash, ash | `~/.profile` |
|
|
68
|
+
|
|
69
|
+
If `$SHELL` is unset or unsupported, envr chooses the first existing file among
|
|
70
|
+
`~/.zshrc`, `~/.bashrc`, and `~/.profile`, falling back to `~/.profile`.
|
|
71
|
+
`--file PATH` overrides this selection. Known filenames select their matching
|
|
72
|
+
validation shell; custom filenames use the supported `$SHELL`, or `sh`.
|
|
73
|
+
The selected shell must be installed to save changes.
|
|
74
|
+
|
|
75
|
+
These are editing defaults, not a guarantee of what every shell startup mode
|
|
76
|
+
loads. Bash login shells may use `.bash_profile`; ksh may require `ENV` to point
|
|
77
|
+
to `.kshrc`. Use `--file` for your setup. Fish/csh syntax is not supported.
|
|
78
|
+
|
|
79
|
+
## Commands
|
|
80
|
+
|
|
81
|
+
| Command | Behavior |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `envr` | Open the local editor in your default browser |
|
|
84
|
+
| `envr --file ~/.bash_profile` | Edit a specific file |
|
|
85
|
+
| `envr --no-browser` | Print the URL without opening a browser |
|
|
86
|
+
| `envr --timeout 120` | Stop after 120 seconds; default `0` means until Ctrl-C |
|
|
87
|
+
| `envr --json` | Print one JSON document with platform/config metadata and variable names, **not values**; no prompts/server |
|
|
88
|
+
| `envr -q` | Unix platform gate only; silent, no config file access |
|
|
89
|
+
| `envr --mac -q` | macOS-only gate; silent, no config file access |
|
|
90
|
+
| `envr --version` | Print the version |
|
|
91
|
+
| `python -m envr` | Same command when using the installed interpreter |
|
|
92
|
+
|
|
93
|
+
Exit codes: `0` = supported platform, `1` = unsupported platform (or server startup
|
|
94
|
+
failure), `2` = invalid usage, `130` = interrupted. Config/marker problems are
|
|
95
|
+
reported but do not change the platform verdict. `--mac` exits `1` off macOS.
|
|
96
|
+
|
|
97
|
+
## Editing rules and safe saves
|
|
98
|
+
|
|
99
|
+
- The **first two** marker lines delimit the block. Put them at top level, not
|
|
100
|
+
inside quoted strings, functions, conditionals, or here-documents.
|
|
101
|
+
- Editable entries are single-line literal `export NAME=value` statements.
|
|
102
|
+
Plain, single-quoted, double-quoted, and escaped literal values are supported.
|
|
103
|
+
- Names use `[A-Za-z_][A-Za-z0-9_]*` (at most 128 characters); values are at most
|
|
104
|
+
4096 characters, without newline, NUL, or invalid Unicode. Duplicate names are
|
|
105
|
+
rejected when adding or editing.
|
|
106
|
+
- Comments and blank lines are preserved. If a block contains shell expressions
|
|
107
|
+
such as `$HOME`, command substitution, multiline values, or other statements,
|
|
108
|
+
the **whole block is read-only**. Edit those blocks manually. Nothing is evaluated
|
|
109
|
+
merely to display the table.
|
|
110
|
+
- Edited values are treated as **literal strings**, safely shell-quoted. Untouched
|
|
111
|
+
entries, inline comments, line endings, and content outside the block stay intact.
|
|
112
|
+
- Existing symlinks are followed; the target is updated without replacing the link.
|
|
113
|
+
The target's Unix permission bits are retained. Atomic replacement changes the
|
|
114
|
+
inode; hard-link relationships, ACLs, and extended attributes are not preserved.
|
|
115
|
+
- External changes are detected before replacement and rejected. Refresh before
|
|
116
|
+
retrying. Use one editor tab/session per file; this is not a multiuser editor.
|
|
117
|
+
|
|
118
|
+
On save, envr writes a private temporary candidate in the same directory, runs
|
|
119
|
+
`<shell> -n` against it, and replaces the live file **only if validation succeeds**.
|
|
120
|
+
If validation fails or the shell is unavailable, the original file is unchanged.
|
|
121
|
+
Temporary files are removed afterward.
|
|
122
|
+
|
|
123
|
+
### Sourcing: important limitation
|
|
124
|
+
|
|
125
|
+
After saving, envr executes `. <file>` in a **child shell** with a 15-second timeout.
|
|
126
|
+
Raw shell output is discarded so secrets cannot leak into HTTP errors or terminal
|
|
127
|
+
logs; the page reports whether sourcing completed successfully.
|
|
128
|
+
|
|
129
|
+
**This executes your entire startup file, including commands outside the block.**
|
|
130
|
+
Use envr only with configurations you trust. Syntax validation is not a sandbox.
|
|
131
|
+
Startup guards may skip parts of a file in a noninteractive child shell.
|
|
132
|
+
|
|
133
|
+
A child process cannot change an already-open terminal's environment. To apply
|
|
134
|
+
changes there, run the appropriate command yourself:
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
source ~/.zshrc # zsh
|
|
138
|
+
source ~/.bashrc # bash
|
|
139
|
+
. ~/.profile # POSIX sh
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Security
|
|
143
|
+
|
|
144
|
+
The server binds **127.0.0.1 only**, uses an OS-assigned port and a random 192-bit
|
|
145
|
+
token in the URL, and rejects foreign Host/Origin headers. It has no external
|
|
146
|
+
assets, analytics, CORS permissions, or runtime package dependencies. Responses
|
|
147
|
+
use `no-store`, `no-referrer`, `nosniff`, frame denial, and a nonce-based script CSP.
|
|
148
|
+
JSON writes are bounded and validated; displayed values are not inserted as HTML.
|
|
149
|
+
|
|
150
|
+
The local URL grants **read/write access** and sourcing permission: do not share
|
|
151
|
+
it. It can remain in browser history and terminal scrollback. This is a local
|
|
152
|
+
convenience tool, not encrypted secret storage or a sandbox against other processes
|
|
153
|
+
running as your user. Close the server when finished. See [SECURITY.md](SECURITY.md).
|
|
154
|
+
|
|
155
|
+
## Development
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
git clone https://github.com/Qwik-Tools/envr.git
|
|
159
|
+
cd envr
|
|
160
|
+
python3 -m venv .venv
|
|
161
|
+
. .venv/bin/activate
|
|
162
|
+
python -m pip install --upgrade pip
|
|
163
|
+
python -m pip install -e . --group dev
|
|
164
|
+
python -m pytest
|
|
165
|
+
ruff check src tests
|
|
166
|
+
ruff format --check src tests
|
|
167
|
+
mypy src/envr
|
|
168
|
+
python -m build
|
|
169
|
+
python -m twine check dist/*
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Tests use temporary config files; they do not edit or source your real startup file.
|
|
173
|
+
Each test has a timeout to prevent accidental indefinite server runs.
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
src/envr/system.py platform detection
|
|
177
|
+
src/envr/shellrc.py config selection, read/create prompt
|
|
178
|
+
src/envr/secretblock.py marker extraction helper
|
|
179
|
+
src/envr/secretfile.py literal parsing, CRUD, validated writes, child sourcing
|
|
180
|
+
src/envr/webview.py loopback HTTP API and browser table
|
|
181
|
+
src/envr/cli.py command-line entry point
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The distribution is `qwik-envr`; Python imports remain under `envr`. The main
|
|
185
|
+
library interface is `SecretStore.load(path, shell=...)`, then `items()`,
|
|
186
|
+
`add()`, `update()`, `delete()`, `save()`, and `reload()`. `StoreError.code` is
|
|
187
|
+
stable; error details never include values. Store access must be serialized by
|
|
188
|
+
the caller (the HTTP server does this). Maintained by Qwik-Tools / Himakar Reddy.
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Report vulnerabilities privately through GitHub's private vulnerability reporting
|
|
4
|
+
for `Qwik-Tools/envr`. If reporting is unavailable, contact a maintainer without
|
|
5
|
+
including real tokens, config files, or tokenized local URLs in a public issue.
|
|
6
|
+
|
|
7
|
+
## Threat model
|
|
8
|
+
|
|
9
|
+
Assets are plaintext environment values, the user's startup file, and commands
|
|
10
|
+
executed when that file is sourced. Trust boundaries are the browser-to-loopback
|
|
11
|
+
HTTP connection, JSON edits, and the filesystem/shell boundary.
|
|
12
|
+
|
|
13
|
+
Foreign websites are denied by a random 192-bit URL capability, Host/Origin
|
|
14
|
+
checks, absence of CORS, JSON-only bounded writes, and CSP/frame restrictions.
|
|
15
|
+
Values are handled as literal strings when edited, not interpolated shell code.
|
|
16
|
+
Syntax checks happen before atomic replacement. Snapshot checks reject external
|
|
17
|
+
changes observed before replacement; this is not a filesystem locking service.
|
|
18
|
+
|
|
19
|
+
## Residual risks
|
|
20
|
+
|
|
21
|
+
- Anyone with the complete URL can read/write exports and trigger sourcing.
|
|
22
|
+
- URLs can remain in browser history or terminal scrollback.
|
|
23
|
+
- The HTTP transport is plaintext over IPv4 loopback; do not proxy or expose it.
|
|
24
|
+
- Other programs running as the same user can read config files, inspect process
|
|
25
|
+
memory, or race filesystem changes. envr does not defend against that actor.
|
|
26
|
+
- Sourcing intentionally executes the entire trusted config with your privileges.
|
|
27
|
+
The timeout is not a sandbox; spawned processes or existing shell commands can
|
|
28
|
+
have side effects. Do not open an untrusted configuration with this tool.
|
|
29
|
+
- Unix permission bits are retained, but atomic replacement does not retain ACLs,
|
|
30
|
+
extended attributes, or hard-link relationships. Use ordinary user-owned UTF-8
|
|
31
|
+
shell config files, not shared or specially permissioned system files.
|
|
32
|
+
- Use one browser tab and envr process per file. Concurrent browser edits are not
|
|
33
|
+
a supported collaboration mode.
|
|
34
|
+
|
|
35
|
+
Only the latest published release is supported. No real secrets should ever be
|
|
36
|
+
included in fixtures, screenshots, bug reports, source archives, or logs.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qwik-envr"
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "A local browser editor for Unix shell environment variables."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Himakar Reddy" }]
|
|
14
|
+
keywords = ["unix", "shell", "environment", "cli", "secrets"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: MacOS",
|
|
19
|
+
"Operating System :: POSIX",
|
|
20
|
+
"Operating System :: POSIX :: BSD",
|
|
21
|
+
"Operating System :: POSIX :: Linux",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Topic :: System :: Systems Administration",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/Qwik-Tools/envr"
|
|
29
|
+
Repository = "https://github.com/Qwik-Tools/envr"
|
|
30
|
+
Issues = "https://github.com/Qwik-Tools/envr/issues"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
envr = "envr.cli:main"
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = ["pytest>=8.0", "pytest-timeout>=2.3", "ruff>=0.14", "mypy>=1.15,<1.20", "build>=1.2", "twine>=6"]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/envr"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.sdist]
|
|
42
|
+
include = ["src/", "tests/", "pyproject.toml", "README.md", "LICENSE", "SECURITY.md"]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
testpaths = ["tests"]
|
|
46
|
+
addopts = "-q"
|
|
47
|
+
timeout = 15
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
target-version = "py39"
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = ["E4", "E7", "E9", "F", "I", "UP", "B"]
|
|
54
|
+
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
python_version = "3.9"
|
|
57
|
+
check_untyped_defs = true
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""envr -- a Unix check, plus a browser editor for your shell secrets block.
|
|
2
|
+
|
|
3
|
+
Public surface:
|
|
4
|
+
|
|
5
|
+
is_unix() -> bool
|
|
6
|
+
is_mac() -> bool
|
|
7
|
+
os_label() -> str
|
|
8
|
+
os_version() -> str
|
|
9
|
+
platform_info() -> dict
|
|
10
|
+
shell_config_path() -> Path
|
|
11
|
+
inspect_shell_config()-> ShellReport
|
|
12
|
+
extract_block() -> str
|
|
13
|
+
SecretStore -- parse/edit/save the export block
|
|
14
|
+
serve_store() -> ServeResult
|
|
15
|
+
|
|
16
|
+
The ``envr`` console script is the only intended entry point; ``python -m envr``
|
|
17
|
+
runs the same command.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from .secretblock import SecretsBlockError, extract_block
|
|
23
|
+
from .secretfile import SecretStore, StoreError, quote_value, unquote_value
|
|
24
|
+
from .shellrc import (
|
|
25
|
+
POSIX_SHELLS,
|
|
26
|
+
ShellReport,
|
|
27
|
+
ShellState,
|
|
28
|
+
config_path_for,
|
|
29
|
+
detect_shell,
|
|
30
|
+
inspect_shell_config,
|
|
31
|
+
shell_config_path,
|
|
32
|
+
shell_name,
|
|
33
|
+
)
|
|
34
|
+
from .system import is_mac, is_unix, os_label, os_version, platform_info
|
|
35
|
+
from .webview import ServeResult, serve_store
|
|
36
|
+
|
|
37
|
+
__version__ = "0.5.0"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"POSIX_SHELLS",
|
|
41
|
+
"SecretStore",
|
|
42
|
+
"SecretsBlockError",
|
|
43
|
+
"ServeResult",
|
|
44
|
+
"ShellReport",
|
|
45
|
+
"ShellState",
|
|
46
|
+
"StoreError",
|
|
47
|
+
"__version__",
|
|
48
|
+
"config_path_for",
|
|
49
|
+
"detect_shell",
|
|
50
|
+
"extract_block",
|
|
51
|
+
"inspect_shell_config",
|
|
52
|
+
"is_mac",
|
|
53
|
+
"is_unix",
|
|
54
|
+
"os_label",
|
|
55
|
+
"os_version",
|
|
56
|
+
"platform_info",
|
|
57
|
+
"quote_value",
|
|
58
|
+
"serve_store",
|
|
59
|
+
"shell_config_path",
|
|
60
|
+
"shell_name",
|
|
61
|
+
"unquote_value",
|
|
62
|
+
]
|