cloudx-proxy 0.17.0__tar.gz → 0.17.2__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.
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/context/development.md +1 -1
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/context/project-overview.md +3 -2
- cloudx_proxy-0.17.2/.github/workflows/test.yml +65 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.gitignore +1 -3
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/CHANGELOG.md +16 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/CLAUDE.md +14 -1
- cloudx_proxy-0.17.2/CONTRIBUTING.md +57 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/PKG-INFO +17 -3
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/README.md +16 -2
- cloudx_proxy-0.17.2/cloudx_proxy/__init__.py +31 -0
- cloudx_proxy-0.17.0/cloudx_proxy/_1password.py → cloudx_proxy-0.17.2/cloudx_proxy/_op.py +34 -42
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy/_version.py +2 -2
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy/cli.py +84 -57
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy/colors.py +1 -14
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy/core.py +42 -26
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy/setup.py +771 -338
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy.egg-info/PKG-INFO +17 -3
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy.egg-info/SOURCES.txt +10 -2
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/pyproject.toml +23 -0
- cloudx_proxy-0.17.2/tests/test_backward_compat.py +334 -0
- cloudx_proxy-0.17.2/tests/test_cli_options.py +65 -0
- cloudx_proxy-0.17.2/tests/test_config_safety.py +287 -0
- cloudx_proxy-0.17.2/tests/test_core.py +157 -0
- cloudx_proxy-0.17.2/tests/test_non_interactive.py +166 -0
- cloudx_proxy-0.17.2/tests/test_runtime_safety.py +263 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/tests/test_setup.py +5 -5
- cloudx_proxy-0.17.2/tests/test_ssh_config.py +470 -0
- cloudx_proxy-0.17.2/tests/test_version.py +73 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/uv.lock +30 -3
- cloudx_proxy-0.17.0/.github/workflows/test.yml +0 -39
- cloudx_proxy-0.17.0/CONTRIBUTING.md +0 -29
- cloudx_proxy-0.17.0/cloudx_proxy/__init__.py +0 -3
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/README.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/context/README.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/context/architecture.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/context/commit-conventions.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/context/ssh-config.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/roadmap/architecture-review.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/roadmap/documentation-findings.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.ai/roadmap/improvement-roadmap.md +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.clinerules +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.envrc +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.github/dependabot.yml +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.github/workflows/claude.yml +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.github/workflows/codeql.yml +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.github/workflows/release.yml +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/.releaserc +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/LICENSE +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/NOTICE +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy.egg-info/dependency_links.txt +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy.egg-info/entry_points.txt +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy.egg-info/requires.txt +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/cloudx_proxy.egg-info/top_level.txt +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/setup.cfg +0 -0
- {cloudx_proxy-0.17.0 → cloudx_proxy-0.17.2}/tests/__init__.py +0 -0
|
@@ -89,7 +89,7 @@ When working on this codebase, prioritize:
|
|
|
89
89
|
1. **Type safety**: Add complete type hints to new code.
|
|
90
90
|
2. **Single responsibility**: Keep classes and methods focused.
|
|
91
91
|
3. **Error handling**: Use specific exceptions with context.
|
|
92
|
-
4. **Testing**: Write tests for new functionality
|
|
92
|
+
4. **Testing**: Write tests for new functionality. Run `uv run pytest` and `uv run ruff check cloudx_proxy tests`; both gate CI. `tests/test_backward_compat.py` holds SSH configs as earlier releases wrote them and must keep passing unchanged - a failure there means existing users would have to edit a file or re-run setup.
|
|
93
93
|
5. **Security**: Validate all inputs and sanitize subprocess calls.
|
|
94
94
|
|
|
95
95
|
## Release Process
|
|
@@ -38,8 +38,9 @@ uvx cloudX-proxy setup [OPTIONS]
|
|
|
38
38
|
- `--aws-env`: AWS environment directory.
|
|
39
39
|
- `--instance`: EC2 instance ID (skips prompt).
|
|
40
40
|
- `--hostname`: Hostname for SSH config.
|
|
41
|
-
- `--
|
|
42
|
-
- `--
|
|
41
|
+
- `--environment`: cloudX environment (default: from the instance `Name` tag).
|
|
42
|
+
- `--yes`: Non-interactive mode; failures exit non-zero instead of being continued past.
|
|
43
|
+
- `--dry-run`: Preview changes; makes no AWS calls and writes no files.
|
|
43
44
|
|
|
44
45
|
**What Setup Does:**
|
|
45
46
|
1. **Configures AWS Profile**: Creates/validates profile for IAM user.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- develop
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
- develop
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v7
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Install project and dev tools
|
|
28
|
+
run: uv sync --group dev
|
|
29
|
+
|
|
30
|
+
- name: Ruff
|
|
31
|
+
run: uv run ruff check --output-format=github cloudx_proxy tests
|
|
32
|
+
|
|
33
|
+
test:
|
|
34
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
strategy:
|
|
37
|
+
fail-fast: false
|
|
38
|
+
matrix:
|
|
39
|
+
python-version:
|
|
40
|
+
- "3.10"
|
|
41
|
+
- "3.11"
|
|
42
|
+
- "3.12"
|
|
43
|
+
- "3.13"
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout
|
|
47
|
+
uses: actions/checkout@v7
|
|
48
|
+
|
|
49
|
+
- name: Install uv
|
|
50
|
+
uses: astral-sh/setup-uv@v7
|
|
51
|
+
with:
|
|
52
|
+
python-version: ${{ matrix.python-version }}
|
|
53
|
+
|
|
54
|
+
- name: Install project and dev tools
|
|
55
|
+
run: uv sync --group dev
|
|
56
|
+
|
|
57
|
+
- name: Run tests
|
|
58
|
+
run: uv run pytest
|
|
59
|
+
|
|
60
|
+
- name: Check the package imports without being built
|
|
61
|
+
# cloudx_proxy/_version.py is generated by setuptools_scm and is not
|
|
62
|
+
# committed, so a plain source checkout must still import
|
|
63
|
+
run: |
|
|
64
|
+
rm -f cloudx_proxy/_version.py
|
|
65
|
+
uv run --no-sync python -c "import cloudx_proxy; print(cloudx_proxy.__version__)"
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [0.17.2](https://github.com/easytocloud/cloudX-proxy/compare/v0.17.1...v0.17.2) (2026-09-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* do not delete a live 1Password agent socket without asking ([5cd724c](https://github.com/easytocloud/cloudX-proxy/commit/5cd724c0cb25f02d440ecf792f54bc1efde557fd))
|
|
7
|
+
* harden the runtime paths around subprocesses ([5f69662](https://github.com/easytocloud/cloudX-proxy/commit/5f69662d6a7da586abc7ec7041c5660a80280297))
|
|
8
|
+
* make --yes and --dry-run report what actually happened ([9a6dcc2](https://github.com/easytocloud/cloudX-proxy/commit/9a6dcc28ecc4f2295aa41f3fedbe396399d52fc2))
|
|
9
|
+
* preserve top-level directives and stop banners accumulating ([ea89703](https://github.com/easytocloud/cloudX-proxy/commit/ea897034fd860a41c1dcf4340d5903244a29e320))
|
|
10
|
+
* repair unquoted IdentityFile paths in existing configs ([e2dc815](https://github.com/easytocloud/cloudX-proxy/commit/e2dc8151d1897236553e5b67ff08d3edf094a1d2))
|
|
11
|
+
* run the AWS CLI without a shell, and resolve CodeQL's import notes ([4962c2c](https://github.com/easytocloud/cloudX-proxy/commit/4962c2c20e5d2c98d13469d09a56430836de268d))
|
|
12
|
+
* stop SSH config rewrites from discarding configuration ([40b1d0f](https://github.com/easytocloud/cloudX-proxy/commit/40b1d0ff9b4c503df6d142b9d2baef6f34579011))
|
|
13
|
+
* validate and quote values written into the SSH config ([bf55191](https://github.com/easytocloud/cloudX-proxy/commit/bf551913be8057f3e1e58599a46b14b6f2d7422b))
|
|
14
|
+
|
|
15
|
+
## [0.17.1](https://github.com/easytocloud/cloudX-proxy/compare/v0.17.0...v0.17.1) (2026-09-02)
|
|
16
|
+
|
|
1
17
|
# [0.17.0](https://github.com/easytocloud/cloudX-proxy/compare/v0.16.15...v0.17.0) (2026-09-02)
|
|
2
18
|
|
|
3
19
|
|
|
@@ -26,6 +26,19 @@ python -m build
|
|
|
26
26
|
pip install dist/cloudx_proxy-*.whl
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
### Testing and Linting
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Run the test suite
|
|
33
|
+
uv run pytest
|
|
34
|
+
|
|
35
|
+
# Lint (config in pyproject.toml under [tool.ruff])
|
|
36
|
+
uv run ruff check cloudx_proxy tests
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`tests/test_backward_compat.py` pins the behaviour existing installations
|
|
40
|
+
depend on; see CONTRIBUTING.md before changing those fixtures.
|
|
41
|
+
|
|
29
42
|
### Running the Application
|
|
30
43
|
|
|
31
44
|
The application is designed to be run via `uvx` (from the `uv` package manager):
|
|
@@ -50,7 +63,7 @@ When working on this codebase, prioritize:
|
|
|
50
63
|
1. **Type safety** - Add complete type hints to new code
|
|
51
64
|
2. **Single responsibility** - Keep classes and methods focused
|
|
52
65
|
3. **Error handling** - Use specific exceptions with context
|
|
53
|
-
4. **Testing** - Write tests for new functionality
|
|
66
|
+
4. **Testing** - Write tests for new functionality; run `uv run pytest` and `uv run ruff check`
|
|
54
67
|
5. **Security** - Validate all inputs and sanitize subprocess calls
|
|
55
68
|
|
|
56
69
|
## Release Process
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Contributing to cloudX-proxy
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
1. Clone the repository
|
|
6
|
+
2. Install the project and development tools:
|
|
7
|
+
```bash
|
|
8
|
+
uv sync --group dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Tests and Linting
|
|
12
|
+
|
|
13
|
+
Both run in CI on every pull request and must pass:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run the test suite
|
|
17
|
+
uv run pytest
|
|
18
|
+
|
|
19
|
+
# Lint (configuration lives in pyproject.toml under [tool.ruff])
|
|
20
|
+
uv run ruff check cloudx_proxy tests
|
|
21
|
+
|
|
22
|
+
# Apply the fixes ruff can make safely
|
|
23
|
+
uv run ruff check --fix cloudx_proxy tests
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Backward compatibility
|
|
27
|
+
|
|
28
|
+
`uvx cloudX-proxy` resolves to the latest release every time it runs, so an
|
|
29
|
+
existing user's SSH config - written by an older version and never edited
|
|
30
|
+
again - is handed straight to new code. `tests/test_backward_compat.py` holds
|
|
31
|
+
configs exactly as earlier versions wrote them and asserts that `cleanup`
|
|
32
|
+
changes nothing in them but the version stamp, that they still parse, and that
|
|
33
|
+
every ProxyCommand shape those versions emitted is still accepted by `connect`.
|
|
34
|
+
|
|
35
|
+
Treat those fixtures as frozen. If a change makes one of them fail, the change
|
|
36
|
+
requires existing users to edit a file or re-run setup, which needs to be a
|
|
37
|
+
deliberate decision rather than a side effect.
|
|
38
|
+
|
|
39
|
+
## Publishing to PyPI
|
|
40
|
+
|
|
41
|
+
The package is automatically published to PyPI via GitHub Actions when a new release is created. Setup:
|
|
42
|
+
|
|
43
|
+
1. Register project on PyPI
|
|
44
|
+
2. Generate API token in PyPI (Account Settings → API tokens)
|
|
45
|
+
3. Add token as GitHub secret named `PYPI_TOKEN`
|
|
46
|
+
|
|
47
|
+
## Versioning
|
|
48
|
+
|
|
49
|
+
The project uses semantic-release for versioning.
|
|
50
|
+
Version numbers are automatically determined based on commit messages following the conventional commits specification.
|
|
51
|
+
|
|
52
|
+
The GitHub Actions workflow will:
|
|
53
|
+
|
|
54
|
+
1. Determine next version based on commits
|
|
55
|
+
2. Update CHANGELOG.md
|
|
56
|
+
3. Create GitHub release
|
|
57
|
+
4. Publish to PyPI
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudx-proxy
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.2
|
|
4
4
|
Summary: SSH proxy command to connect VSCode with cloudX instance using AWS Systems Manager
|
|
5
5
|
Author-email: easytocloud <info@easytocloud.com>
|
|
6
6
|
License: MIT License
|
|
@@ -282,6 +282,14 @@ The setup command will:
|
|
|
282
282
|
- Offers to wait for setup completion
|
|
283
283
|
- Monitors setup progress
|
|
284
284
|
|
|
285
|
+
### Prerequisites Check
|
|
286
|
+
|
|
287
|
+
`setup` first checks that the AWS CLI and the Session Manager plugin are on your
|
|
288
|
+
PATH. Both are invoked from inside the SSH ProxyCommand, where a missing binary
|
|
289
|
+
surfaces only as "Could not establish connection" in VSCode, so setup reports it
|
|
290
|
+
up front. Setup continues either way - writing the configuration is still useful -
|
|
291
|
+
but connections will fail until they are installed.
|
|
292
|
+
|
|
285
293
|
### SSH Configuration
|
|
286
294
|
|
|
287
295
|
The setup command configures SSH to use cloudX-proxy as a ProxyCommand, enabling seamless connections through AWS Systems Manager. For example, running:
|
|
@@ -492,8 +500,9 @@ Options:
|
|
|
492
500
|
- `--aws-env` (optional): AWS environment directory to use. If specified, AWS configuration and credentials will be read from ~/.aws/aws-envs/{env}/.
|
|
493
501
|
- `--instance` (optional): EC2 instance ID to set up connection for. If provided, skips the instance ID prompt.
|
|
494
502
|
- `--hostname` (optional): Hostname to use for SSH configuration. If not provided, a hostname will be generated from the instance ID in non-interactive mode or prompted for in interactive mode.
|
|
495
|
-
- `--
|
|
496
|
-
- `--
|
|
503
|
+
- `--environment` (optional): cloudX environment (e.g. `dev`, `pre-prod`). If not provided, it is taken from the instance's `Name` tag (`cloudX-{env}-{hostname}`), then from the AWS user name. Required with `--yes` when neither is available.
|
|
504
|
+
- `--yes` (flag): Non-interactive mode, use default values for all prompts. Requires sufficient defaults or explicit parameters for all required values. Unlike interactive mode, a failure is never continued past: setup reports the error and exits non-zero rather than carrying on.
|
|
505
|
+
- `--dry-run` (flag): Preview setup changes without actually executing them. Makes no AWS calls and writes no files, so it works without credentials.
|
|
497
506
|
|
|
498
507
|
Example usage:
|
|
499
508
|
```bash
|
|
@@ -585,6 +594,11 @@ The cleanup command reorganizes and normalizes your SSH configuration file. It:
|
|
|
585
594
|
- Reorganizes the config with proper structure and banners
|
|
586
595
|
- Normalizes all `cloudX`/`cloudx` prefixes to match the command used
|
|
587
596
|
- Rebuilds ProxyCommand entries to remove redundant flags
|
|
597
|
+
- Preserves entries it does not manage (your own `Host` and `Match` blocks,
|
|
598
|
+
with the comments above them) in a clearly marked section at the end
|
|
599
|
+
- Writes the previous contents to `<config>.bak` before rewriting
|
|
600
|
+
|
|
601
|
+
`cleanup` performs a full rewrite of the file, so the backup is your undo.
|
|
588
602
|
|
|
589
603
|
Options:
|
|
590
604
|
- `--ssh-config` (optional): Path to the SSH config file to use. If not specified, uses ~/.ssh/cloudX/config.
|
|
@@ -230,6 +230,14 @@ The setup command will:
|
|
|
230
230
|
- Offers to wait for setup completion
|
|
231
231
|
- Monitors setup progress
|
|
232
232
|
|
|
233
|
+
### Prerequisites Check
|
|
234
|
+
|
|
235
|
+
`setup` first checks that the AWS CLI and the Session Manager plugin are on your
|
|
236
|
+
PATH. Both are invoked from inside the SSH ProxyCommand, where a missing binary
|
|
237
|
+
surfaces only as "Could not establish connection" in VSCode, so setup reports it
|
|
238
|
+
up front. Setup continues either way - writing the configuration is still useful -
|
|
239
|
+
but connections will fail until they are installed.
|
|
240
|
+
|
|
233
241
|
### SSH Configuration
|
|
234
242
|
|
|
235
243
|
The setup command configures SSH to use cloudX-proxy as a ProxyCommand, enabling seamless connections through AWS Systems Manager. For example, running:
|
|
@@ -440,8 +448,9 @@ Options:
|
|
|
440
448
|
- `--aws-env` (optional): AWS environment directory to use. If specified, AWS configuration and credentials will be read from ~/.aws/aws-envs/{env}/.
|
|
441
449
|
- `--instance` (optional): EC2 instance ID to set up connection for. If provided, skips the instance ID prompt.
|
|
442
450
|
- `--hostname` (optional): Hostname to use for SSH configuration. If not provided, a hostname will be generated from the instance ID in non-interactive mode or prompted for in interactive mode.
|
|
443
|
-
- `--
|
|
444
|
-
- `--
|
|
451
|
+
- `--environment` (optional): cloudX environment (e.g. `dev`, `pre-prod`). If not provided, it is taken from the instance's `Name` tag (`cloudX-{env}-{hostname}`), then from the AWS user name. Required with `--yes` when neither is available.
|
|
452
|
+
- `--yes` (flag): Non-interactive mode, use default values for all prompts. Requires sufficient defaults or explicit parameters for all required values. Unlike interactive mode, a failure is never continued past: setup reports the error and exits non-zero rather than carrying on.
|
|
453
|
+
- `--dry-run` (flag): Preview setup changes without actually executing them. Makes no AWS calls and writes no files, so it works without credentials.
|
|
445
454
|
|
|
446
455
|
Example usage:
|
|
447
456
|
```bash
|
|
@@ -533,6 +542,11 @@ The cleanup command reorganizes and normalizes your SSH configuration file. It:
|
|
|
533
542
|
- Reorganizes the config with proper structure and banners
|
|
534
543
|
- Normalizes all `cloudX`/`cloudx` prefixes to match the command used
|
|
535
544
|
- Rebuilds ProxyCommand entries to remove redundant flags
|
|
545
|
+
- Preserves entries it does not manage (your own `Host` and `Match` blocks,
|
|
546
|
+
with the comments above them) in a clearly marked section at the end
|
|
547
|
+
- Writes the previous contents to `<config>.bak` before rewriting
|
|
548
|
+
|
|
549
|
+
`cleanup` performs a full rewrite of the file, so the backup is your undo.
|
|
536
550
|
|
|
537
551
|
Options:
|
|
538
552
|
- `--ssh-config` (optional): Path to the SSH config file to use. If not specified, uses ~/.ssh/cloudX/config.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""cloudx-proxy - SSH proxy connecting VSCode Remote SSH to EC2 via AWS SSM."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _resolve_version() -> str:
|
|
5
|
+
"""Determine the package version.
|
|
6
|
+
|
|
7
|
+
``_version.py`` is written by setuptools_scm at build time and is not
|
|
8
|
+
committed, so it is absent in a plain source checkout. Fall back to the
|
|
9
|
+
installed distribution metadata, and finally to a sentinel, so importing
|
|
10
|
+
the package never fails merely because it has not been built.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
str: The package version
|
|
14
|
+
"""
|
|
15
|
+
try:
|
|
16
|
+
from ._version import __version__ as scm_version
|
|
17
|
+
return scm_version
|
|
18
|
+
except ImportError:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
return version("cloudx-proxy")
|
|
25
|
+
except PackageNotFoundError:
|
|
26
|
+
return "0.0.0+unknown"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__version__ = _resolve_version()
|
|
30
|
+
|
|
31
|
+
__all__ = ['__version__']
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
"""1Password integration, via 1Password's own CLI (`op`).
|
|
2
|
+
|
|
3
|
+
Naming rule: every identifier for this integration is `op_*`, after the `op`
|
|
4
|
+
CLI - never `use_1password`, `onepassword_agent_sock` or similar. User-facing
|
|
5
|
+
text still says "1Password"; only identifiers avoid the word.
|
|
6
|
+
|
|
7
|
+
That is not only style. CodeQL's py/clear-text-logging-sensitive-data query
|
|
8
|
+
decides whether a value is a credential from the name of the identifier that
|
|
9
|
+
produced it, and it matches "password" as a substring - which both "1password"
|
|
10
|
+
and "onepassword" contain. Naming things after the product therefore marked
|
|
11
|
+
vault names, the agent socket path and the `op --version` string as secrets,
|
|
12
|
+
and every status message printing one was reported as clear-text logging of a
|
|
13
|
+
password: high severity, and wrong every time. Renaming to `op_*` describes
|
|
14
|
+
the values accurately and stops the misclassification at its source.
|
|
15
|
+
"""
|
|
16
|
+
|
|
2
17
|
import json
|
|
18
|
+
import os
|
|
3
19
|
import subprocess
|
|
4
20
|
|
|
5
|
-
|
|
21
|
+
# The 1Password CLI can block on an interactive unlock (biometrics, or a
|
|
22
|
+
# device prompt). Output is captured here, so that prompt would be invisible
|
|
23
|
+
# and the call would hang; fail after a bounded wait instead.
|
|
24
|
+
OP_TIMEOUT = 30
|
|
25
|
+
|
|
26
|
+
def check_op_cli() -> tuple:
|
|
6
27
|
"""Check if 1Password CLI is installed and authenticated.
|
|
7
28
|
|
|
8
29
|
Returns:
|
|
@@ -14,7 +35,8 @@ def check_1password_cli() -> tuple:
|
|
|
14
35
|
['op', '--version'],
|
|
15
36
|
capture_output=True,
|
|
16
37
|
text=True,
|
|
17
|
-
check=False
|
|
38
|
+
check=False,
|
|
39
|
+
timeout=OP_TIMEOUT
|
|
18
40
|
)
|
|
19
41
|
|
|
20
42
|
if result.returncode != 0:
|
|
@@ -27,7 +49,8 @@ def check_1password_cli() -> tuple:
|
|
|
27
49
|
['op', 'account', 'list'],
|
|
28
50
|
capture_output=True,
|
|
29
51
|
text=True,
|
|
30
|
-
check=False
|
|
52
|
+
check=False,
|
|
53
|
+
timeout=OP_TIMEOUT
|
|
31
54
|
)
|
|
32
55
|
|
|
33
56
|
if result.returncode != 0:
|
|
@@ -40,40 +63,6 @@ def check_1password_cli() -> tuple:
|
|
|
40
63
|
except Exception:
|
|
41
64
|
return False, False, ""
|
|
42
65
|
|
|
43
|
-
def check_ssh_agent(agent_sock_path: str) -> bool:
|
|
44
|
-
"""Check if 1Password SSH agent is running.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
agent_sock_path: Path to the SSH agent socket
|
|
48
|
-
|
|
49
|
-
Returns:
|
|
50
|
-
bool: True if agent is running
|
|
51
|
-
"""
|
|
52
|
-
try:
|
|
53
|
-
# Check if the socket file exists
|
|
54
|
-
if not os.path.exists(agent_sock_path):
|
|
55
|
-
return False
|
|
56
|
-
|
|
57
|
-
# Check if agent is active
|
|
58
|
-
env = os.environ.copy()
|
|
59
|
-
env['SSH_AUTH_SOCK'] = agent_sock_path
|
|
60
|
-
|
|
61
|
-
result = subprocess.run(
|
|
62
|
-
['ssh-add', '-l'],
|
|
63
|
-
env=env,
|
|
64
|
-
capture_output=True,
|
|
65
|
-
text=True,
|
|
66
|
-
check=False
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
if "Could not open a connection to your authentication agent" in result.stderr:
|
|
70
|
-
return False
|
|
71
|
-
|
|
72
|
-
return True
|
|
73
|
-
|
|
74
|
-
except Exception:
|
|
75
|
-
return False
|
|
76
|
-
|
|
77
66
|
def list_ssh_keys() -> list:
|
|
78
67
|
"""List SSH keys stored in 1Password.
|
|
79
68
|
|
|
@@ -86,7 +75,8 @@ def list_ssh_keys() -> list:
|
|
|
86
75
|
['op', 'item', 'list', '--categories', 'SSH Key', '--format=json'],
|
|
87
76
|
capture_output=True,
|
|
88
77
|
text=True,
|
|
89
|
-
check=False
|
|
78
|
+
check=False,
|
|
79
|
+
timeout=OP_TIMEOUT
|
|
90
80
|
)
|
|
91
81
|
|
|
92
82
|
if result.returncode != 0:
|
|
@@ -119,7 +109,8 @@ def create_ssh_key(title: str, vault: str) -> tuple:
|
|
|
119
109
|
],
|
|
120
110
|
capture_output=True,
|
|
121
111
|
text=True,
|
|
122
|
-
check=False
|
|
112
|
+
check=False,
|
|
113
|
+
timeout=OP_TIMEOUT
|
|
123
114
|
)
|
|
124
115
|
|
|
125
116
|
if result.returncode != 0:
|
|
@@ -130,7 +121,7 @@ def create_ssh_key(title: str, vault: str) -> tuple:
|
|
|
130
121
|
item_id = ""
|
|
131
122
|
public_key = ""
|
|
132
123
|
|
|
133
|
-
for
|
|
124
|
+
for _idx, line in enumerate(output_lines):
|
|
134
125
|
if line.startswith("ID:"):
|
|
135
126
|
item_id = line.split(":", 1)[1].strip()
|
|
136
127
|
|
|
@@ -166,7 +157,8 @@ def get_vaults() -> list:
|
|
|
166
157
|
['op', 'vault', 'list', '--format=json'],
|
|
167
158
|
capture_output=True,
|
|
168
159
|
text=True,
|
|
169
|
-
check=False
|
|
160
|
+
check=False,
|
|
161
|
+
timeout=OP_TIMEOUT
|
|
170
162
|
)
|
|
171
163
|
|
|
172
164
|
if result.returncode != 0:
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.17.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 17,
|
|
21
|
+
__version__ = version = '0.17.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 17, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|