plexavo 0.2.0__tar.gz → 0.2.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.
- {plexavo-0.2.0 → plexavo-0.2.2}/PKG-INFO +75 -15
- {plexavo-0.2.0 → plexavo-0.2.2}/README.md +74 -14
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/__init__.py +1 -1
- plexavo-0.2.2/plexavo/__main__.py +13 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/auth.py +9 -1
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/interactive.py +42 -12
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo.egg-info/PKG-INFO +75 -15
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo.egg-info/SOURCES.txt +3 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/pyproject.toml +1 -1
- plexavo-0.2.2/tests/test_auth_offline.py +91 -0
- plexavo-0.2.2/tests/test_cli_entrypoint.py +50 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/LICENSE +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/aws_profile_setup.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/__init__.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/encryption.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/iam.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/iam_hygiene.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/logging.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/network.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/storage.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/checks/usage.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/cli.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/findings.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/principals.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/__init__.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/ai_narration.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/fonts/DejaVuSans-Bold.ttf +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/fonts/DejaVuSans-BoldOblique.ttf +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/fonts/DejaVuSans-Oblique.ttf +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/fonts/DejaVuSans.ttf +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/fonts/GEIST-FONT-LICENSE.txt +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/html_report.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/pdf.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/report/templates/report.html.j2 +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo/scoring.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo.egg-info/dependency_links.txt +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo.egg-info/entry_points.txt +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo.egg-info/requires.txt +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/plexavo.egg-info/top_level.txt +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/setup.cfg +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_ai_narration_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_encryption_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_iam_hygiene_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_iam_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_logging_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_network_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_report_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_scoring.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_storage_offline.py +0 -0
- {plexavo-0.2.0 → plexavo-0.2.2}/tests/test_usage_offline.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plexavo
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Open-source AWS misconfiguration scanner — runs with your own local AWS credentials, nothing sent to anyone else.
|
|
5
5
|
Author: Kavee
|
|
6
6
|
License: AGPL-3.0-or-later
|
|
@@ -127,20 +127,24 @@ scoped by a Condition block, or a CloudTrail lookup that hit its page cap on
|
|
|
127
127
|
|
|
128
128
|
## Quick start
|
|
129
129
|
|
|
130
|
-
**
|
|
131
|
-
|
|
132
|
-
or
|
|
130
|
+
The install path depends on your OS — **uv on macOS & Linux**, **pip on
|
|
131
|
+
Windows**. Either way it's a one-command install with no venv to create,
|
|
132
|
+
activate, or reactivate in every new terminal.
|
|
133
133
|
|
|
134
|
-
###
|
|
134
|
+
### macOS & Linux — uv
|
|
135
|
+
|
|
136
|
+
[uv](https://docs.astral.sh/uv/) installs Plexavo into its own isolated
|
|
137
|
+
environment automatically.
|
|
138
|
+
|
|
139
|
+
**1. Install uv** (if you don't already have it):
|
|
135
140
|
|
|
136
141
|
```bash
|
|
137
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
138
|
-
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # Windows
|
|
142
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
139
143
|
```
|
|
140
144
|
|
|
141
145
|
(Full options: [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/).)
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
**2. Install Plexavo:**
|
|
144
148
|
|
|
145
149
|
```bash
|
|
146
150
|
uv tool install plexavo
|
|
@@ -178,12 +182,60 @@ uv tool upgrade plexavo
|
|
|
178
182
|
uv tool uninstall plexavo
|
|
179
183
|
```
|
|
180
184
|
|
|
181
|
-
###
|
|
185
|
+
### Windows — pip
|
|
186
|
+
|
|
187
|
+
Install with `pip` and run the tool as `py -m plexavo`:
|
|
188
|
+
|
|
189
|
+
```powershell
|
|
190
|
+
py -m pip install --user plexavo
|
|
191
|
+
py -m plexavo
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`py -m plexavo` with no arguments opens the interactive menu, exactly
|
|
195
|
+
like the `plexavo` command does on macOS/Linux — flags are only for
|
|
196
|
+
scripting/CI.
|
|
197
|
+
|
|
198
|
+
> **Why not uv/pipx on Windows?** Both work by putting a small generated
|
|
199
|
+
> `plexavo.exe` launcher on your PATH. That launcher is unsigned, and
|
|
200
|
+
> Windows Smart App Control blocks unsigned executables it doesn't
|
|
201
|
+
> recognise — so `plexavo` can fail to start with a "can't confirm who
|
|
202
|
+
> published" message. `py -m plexavo` calls Python directly and never
|
|
203
|
+
> touches that launcher, so it always works. (If your machine doesn't
|
|
204
|
+
> enforce Smart App Control, `uv tool install plexavo` works here too.)
|
|
205
|
+
|
|
206
|
+
**AI-narrated explanations** (see [Cost](#cost)):
|
|
207
|
+
|
|
208
|
+
```powershell
|
|
209
|
+
py -m pip install --user "plexavo[ai]"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Updating or removing later:
|
|
213
|
+
|
|
214
|
+
```powershell
|
|
215
|
+
py -m pip install --user --upgrade plexavo
|
|
216
|
+
py -m pip uninstall plexavo
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Optional: type `plexavo` instead of `py -m plexavo`
|
|
220
|
+
|
|
221
|
+
Add a shortcut to your PowerShell profile once:
|
|
222
|
+
|
|
223
|
+
```powershell
|
|
224
|
+
Add-Content $PROFILE 'function plexavo { py -m plexavo @args }'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Open a new terminal and `plexavo` then works just like it does on
|
|
228
|
+
macOS/Linux.
|
|
229
|
+
|
|
230
|
+
### Run a scan
|
|
182
231
|
|
|
183
232
|
```bash
|
|
184
233
|
plexavo scan --profile my-aws-profile --report-html report.html
|
|
185
234
|
```
|
|
186
235
|
|
|
236
|
+
(On Windows without the shortcut above: `py -m plexavo scan --profile
|
|
237
|
+
my-aws-profile --report-html report.html`.)
|
|
238
|
+
|
|
187
239
|
No `--profile`? It uses your default profile / environment variables,
|
|
188
240
|
same resolution order as the AWS CLI. No AI, no API key, no cost — and
|
|
189
241
|
findings still come with free Next Step / Full Fix Detail guidance
|
|
@@ -213,19 +265,24 @@ plexavo scan --profile my-aws-profile --explain --report-html report.html --repo
|
|
|
213
265
|
|
|
214
266
|
### Alternative: pipx
|
|
215
267
|
|
|
216
|
-
Already use [pipx](https://pipx.pypa.io/)? It works exactly
|
|
217
|
-
its own isolated environment, one global command, no
|
|
268
|
+
Already use [pipx](https://pipx.pypa.io/) on macOS/Linux? It works exactly
|
|
269
|
+
the same way as uv — its own isolated environment, one global command, no
|
|
270
|
+
venv:
|
|
218
271
|
|
|
219
272
|
```bash
|
|
220
273
|
pipx install plexavo
|
|
221
274
|
pipx install "plexavo[ai]" # with AI-narrated explanations
|
|
222
275
|
```
|
|
223
276
|
|
|
277
|
+
On Windows, pipx has the same Smart App Control caveat as uv (see the
|
|
278
|
+
Windows section above) — if the `plexavo` command is blocked, run
|
|
279
|
+
`py -m plexavo` instead, or use the `pip` install.
|
|
280
|
+
|
|
224
281
|
### Alternative: pip + venv
|
|
225
282
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
283
|
+
Want full isolation and prefer to manage the environment yourself? Modern
|
|
284
|
+
Python (PEP 668) blocks a plain `pip install` outside a venv on many
|
|
285
|
+
Linux and Homebrew setups, so create one first:
|
|
229
286
|
|
|
230
287
|
```bash
|
|
231
288
|
python -m venv plexavo-env
|
|
@@ -238,7 +295,9 @@ pip install "plexavo[ai]" # with AI-narrated explanations
|
|
|
238
295
|
|
|
239
296
|
You'll need to reactivate this venv (`plexavo-env\Scripts\activate` /
|
|
240
297
|
`source plexavo-env/bin/activate`) every time you open a new terminal —
|
|
241
|
-
`deactivate` exits it without uninstalling anything.
|
|
298
|
+
`deactivate` exits it without uninstalling anything. On Windows, run
|
|
299
|
+
`python -m plexavo` if the `plexavo` command is blocked by Smart App
|
|
300
|
+
Control.
|
|
242
301
|
|
|
243
302
|
### From source (for contributing, or trying an unreleased change)
|
|
244
303
|
|
|
@@ -257,6 +316,7 @@ plexavo/
|
|
|
257
316
|
├── findings.py # Finding data model, Severity enum
|
|
258
317
|
├── scoring.py # 0-100 score from a list of Findings
|
|
259
318
|
├── cli.py # `plexavo scan ...` entry point
|
|
319
|
+
├── __main__.py # lets `python -m plexavo` run the CLI
|
|
260
320
|
├── checks/
|
|
261
321
|
│ ├── iam.py # IAM-01 to IAM-06 (privilege escalation)
|
|
262
322
|
│ ├── iam_hygiene.py # IAM-07 to IAM-14 (hygiene, cross-account trust)
|
|
@@ -98,20 +98,24 @@ scoped by a Condition block, or a CloudTrail lookup that hit its page cap on
|
|
|
98
98
|
|
|
99
99
|
## Quick start
|
|
100
100
|
|
|
101
|
-
**
|
|
102
|
-
|
|
103
|
-
or
|
|
101
|
+
The install path depends on your OS — **uv on macOS & Linux**, **pip on
|
|
102
|
+
Windows**. Either way it's a one-command install with no venv to create,
|
|
103
|
+
activate, or reactivate in every new terminal.
|
|
104
104
|
|
|
105
|
-
###
|
|
105
|
+
### macOS & Linux — uv
|
|
106
|
+
|
|
107
|
+
[uv](https://docs.astral.sh/uv/) installs Plexavo into its own isolated
|
|
108
|
+
environment automatically.
|
|
109
|
+
|
|
110
|
+
**1. Install uv** (if you don't already have it):
|
|
106
111
|
|
|
107
112
|
```bash
|
|
108
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
109
|
-
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # Windows
|
|
113
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
110
114
|
```
|
|
111
115
|
|
|
112
116
|
(Full options: [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/).)
|
|
113
117
|
|
|
114
|
-
|
|
118
|
+
**2. Install Plexavo:**
|
|
115
119
|
|
|
116
120
|
```bash
|
|
117
121
|
uv tool install plexavo
|
|
@@ -149,12 +153,60 @@ uv tool upgrade plexavo
|
|
|
149
153
|
uv tool uninstall plexavo
|
|
150
154
|
```
|
|
151
155
|
|
|
152
|
-
###
|
|
156
|
+
### Windows — pip
|
|
157
|
+
|
|
158
|
+
Install with `pip` and run the tool as `py -m plexavo`:
|
|
159
|
+
|
|
160
|
+
```powershell
|
|
161
|
+
py -m pip install --user plexavo
|
|
162
|
+
py -m plexavo
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`py -m plexavo` with no arguments opens the interactive menu, exactly
|
|
166
|
+
like the `plexavo` command does on macOS/Linux — flags are only for
|
|
167
|
+
scripting/CI.
|
|
168
|
+
|
|
169
|
+
> **Why not uv/pipx on Windows?** Both work by putting a small generated
|
|
170
|
+
> `plexavo.exe` launcher on your PATH. That launcher is unsigned, and
|
|
171
|
+
> Windows Smart App Control blocks unsigned executables it doesn't
|
|
172
|
+
> recognise — so `plexavo` can fail to start with a "can't confirm who
|
|
173
|
+
> published" message. `py -m plexavo` calls Python directly and never
|
|
174
|
+
> touches that launcher, so it always works. (If your machine doesn't
|
|
175
|
+
> enforce Smart App Control, `uv tool install plexavo` works here too.)
|
|
176
|
+
|
|
177
|
+
**AI-narrated explanations** (see [Cost](#cost)):
|
|
178
|
+
|
|
179
|
+
```powershell
|
|
180
|
+
py -m pip install --user "plexavo[ai]"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Updating or removing later:
|
|
184
|
+
|
|
185
|
+
```powershell
|
|
186
|
+
py -m pip install --user --upgrade plexavo
|
|
187
|
+
py -m pip uninstall plexavo
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Optional: type `plexavo` instead of `py -m plexavo`
|
|
191
|
+
|
|
192
|
+
Add a shortcut to your PowerShell profile once:
|
|
193
|
+
|
|
194
|
+
```powershell
|
|
195
|
+
Add-Content $PROFILE 'function plexavo { py -m plexavo @args }'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Open a new terminal and `plexavo` then works just like it does on
|
|
199
|
+
macOS/Linux.
|
|
200
|
+
|
|
201
|
+
### Run a scan
|
|
153
202
|
|
|
154
203
|
```bash
|
|
155
204
|
plexavo scan --profile my-aws-profile --report-html report.html
|
|
156
205
|
```
|
|
157
206
|
|
|
207
|
+
(On Windows without the shortcut above: `py -m plexavo scan --profile
|
|
208
|
+
my-aws-profile --report-html report.html`.)
|
|
209
|
+
|
|
158
210
|
No `--profile`? It uses your default profile / environment variables,
|
|
159
211
|
same resolution order as the AWS CLI. No AI, no API key, no cost — and
|
|
160
212
|
findings still come with free Next Step / Full Fix Detail guidance
|
|
@@ -184,19 +236,24 @@ plexavo scan --profile my-aws-profile --explain --report-html report.html --repo
|
|
|
184
236
|
|
|
185
237
|
### Alternative: pipx
|
|
186
238
|
|
|
187
|
-
Already use [pipx](https://pipx.pypa.io/)? It works exactly
|
|
188
|
-
its own isolated environment, one global command, no
|
|
239
|
+
Already use [pipx](https://pipx.pypa.io/) on macOS/Linux? It works exactly
|
|
240
|
+
the same way as uv — its own isolated environment, one global command, no
|
|
241
|
+
venv:
|
|
189
242
|
|
|
190
243
|
```bash
|
|
191
244
|
pipx install plexavo
|
|
192
245
|
pipx install "plexavo[ai]" # with AI-narrated explanations
|
|
193
246
|
```
|
|
194
247
|
|
|
248
|
+
On Windows, pipx has the same Smart App Control caveat as uv (see the
|
|
249
|
+
Windows section above) — if the `plexavo` command is blocked, run
|
|
250
|
+
`py -m plexavo` instead, or use the `pip` install.
|
|
251
|
+
|
|
195
252
|
### Alternative: pip + venv
|
|
196
253
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
254
|
+
Want full isolation and prefer to manage the environment yourself? Modern
|
|
255
|
+
Python (PEP 668) blocks a plain `pip install` outside a venv on many
|
|
256
|
+
Linux and Homebrew setups, so create one first:
|
|
200
257
|
|
|
201
258
|
```bash
|
|
202
259
|
python -m venv plexavo-env
|
|
@@ -209,7 +266,9 @@ pip install "plexavo[ai]" # with AI-narrated explanations
|
|
|
209
266
|
|
|
210
267
|
You'll need to reactivate this venv (`plexavo-env\Scripts\activate` /
|
|
211
268
|
`source plexavo-env/bin/activate`) every time you open a new terminal —
|
|
212
|
-
`deactivate` exits it without uninstalling anything.
|
|
269
|
+
`deactivate` exits it without uninstalling anything. On Windows, run
|
|
270
|
+
`python -m plexavo` if the `plexavo` command is blocked by Smart App
|
|
271
|
+
Control.
|
|
213
272
|
|
|
214
273
|
### From source (for contributing, or trying an unreleased change)
|
|
215
274
|
|
|
@@ -228,6 +287,7 @@ plexavo/
|
|
|
228
287
|
├── findings.py # Finding data model, Severity enum
|
|
229
288
|
├── scoring.py # 0-100 score from a list of Findings
|
|
230
289
|
├── cli.py # `plexavo scan ...` entry point
|
|
290
|
+
├── __main__.py # lets `python -m plexavo` run the CLI
|
|
231
291
|
├── checks/
|
|
232
292
|
│ ├── iam.py # IAM-01 to IAM-06 (privilege escalation)
|
|
233
293
|
│ ├── iam_hygiene.py # IAM-07 to IAM-14 (hygiene, cross-account trust)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Allow `python -m plexavo` (and `py -m plexavo` on Windows) to launch the CLI.
|
|
2
|
+
|
|
3
|
+
This is the exact same entry point as the installed `plexavo` command, so
|
|
4
|
+
running it with no arguments still opens the interactive menu. It exists so
|
|
5
|
+
Windows users can install with plain `pip` and run the tool without going
|
|
6
|
+
through the generated `plexavo.exe` launcher, which Windows Smart App Control
|
|
7
|
+
blocks on some machines (it's unsigned). See the README's Quick start.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from plexavo.cli import main
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
main()
|
|
@@ -11,7 +11,7 @@ your own credentials, and nothing leaves your machine.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import boto3
|
|
14
|
-
from botocore.exceptions import NoCredentialsError, ProfileNotFound
|
|
14
|
+
from botocore.exceptions import ClientError, NoCredentialsError, ProfileNotFound
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def get_local_session(profile_name: str | None = None, region: str | None = None) -> boto3.Session:
|
|
@@ -50,6 +50,14 @@ def get_local_session(profile_name: str | None = None, region: str | None = None
|
|
|
50
50
|
f"{f'--profile {profile_name} ' if profile_name else ''}"
|
|
51
51
|
"to set them up."
|
|
52
52
|
) from e
|
|
53
|
+
except ClientError as e:
|
|
54
|
+
code = e.response.get("Error", {}).get("Code", "Unknown")
|
|
55
|
+
raise RuntimeError(
|
|
56
|
+
f"AWS rejected these credentials ({code}). The access key may "
|
|
57
|
+
"be invalid, deactivated, or deleted — run `aws configure "
|
|
58
|
+
f"{f'--profile {profile_name} ' if profile_name else ''}"
|
|
59
|
+
"` to update them."
|
|
60
|
+
) from e
|
|
53
61
|
|
|
54
62
|
return boto3.Session(profile_name=profile_name, region_name=resolved_region)
|
|
55
63
|
|
|
@@ -16,10 +16,10 @@ import os
|
|
|
16
16
|
|
|
17
17
|
import boto3
|
|
18
18
|
from rich import box
|
|
19
|
-
from rich.
|
|
20
|
-
from rich.console import Console
|
|
19
|
+
from rich.console import Console, Group
|
|
21
20
|
from rich.panel import Panel
|
|
22
21
|
from rich.prompt import Confirm, IntPrompt, Prompt
|
|
22
|
+
from rich.table import Table
|
|
23
23
|
from rich.text import Text
|
|
24
24
|
|
|
25
25
|
from plexavo import __version__
|
|
@@ -42,27 +42,57 @@ FEATURES = [
|
|
|
42
42
|
"100% local scanning — your AWS credentials never leave this machine",
|
|
43
43
|
]
|
|
44
44
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
def _gradient_wordmark(word: str, min_shade: int = 90, max_shade: int = 255) -> Text:
|
|
46
|
+
"""Letter-spaced word with a symmetric grey -> white -> grey shimmer,
|
|
47
|
+
one shade per letter (a smooth peak at the center, not a linear ramp),
|
|
48
|
+
e.g. "P L E X A V O" fading brightest at the middle letter."""
|
|
49
|
+
text = Text()
|
|
50
|
+
n = len(word)
|
|
51
|
+
for i, ch in enumerate(word):
|
|
52
|
+
t = 1 - abs((i / (n - 1)) * 2 - 1) if n > 1 else 1.0
|
|
53
|
+
shade = round(min_shade + t * (max_shade - min_shade))
|
|
54
|
+
text.append(ch, style=f"bold #{shade:02x}{shade:02x}{shade:02x}")
|
|
55
|
+
if i != n - 1:
|
|
56
|
+
text.append(" ")
|
|
57
|
+
return text
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
CLOUD_ART = (
|
|
61
|
+
" .--. \n"
|
|
62
|
+
" .-( ). \n"
|
|
63
|
+
" (___.__)__) "
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _header_row() -> Table:
|
|
68
|
+
"""Wordmark pinned to the left corner, a small ASCII cloud floating
|
|
69
|
+
in the opposite corner — a quiet logomark, not a centerpiece."""
|
|
70
|
+
wordmark = _gradient_wordmark("PLEXAVO")
|
|
71
|
+
wordmark.append(" ")
|
|
72
|
+
wordmark.append(f"v{__version__}", style="grey62")
|
|
73
|
+
|
|
74
|
+
grid = Table.grid(expand=True)
|
|
75
|
+
grid.add_column(ratio=1)
|
|
76
|
+
grid.add_column(justify="right")
|
|
77
|
+
grid.add_row(wordmark, Text(CLOUD_ART, style="grey50"))
|
|
78
|
+
return grid
|
|
79
|
+
|
|
50
80
|
|
|
81
|
+
def _splash_panel() -> Panel:
|
|
51
82
|
body = Text()
|
|
52
|
-
body.append_text(header)
|
|
53
|
-
body.append("\n")
|
|
54
83
|
body.append("AWS Security Reimagined", style="italic grey74")
|
|
55
84
|
body.append("\n")
|
|
56
85
|
body.append(WEBSITE, style="underline grey62")
|
|
57
86
|
body.append("\n\n")
|
|
58
|
-
body.append("
|
|
87
|
+
body.append("─" * 50, style="grey42")
|
|
88
|
+
body.append("\n\n")
|
|
59
89
|
for i, feature in enumerate(FEATURES):
|
|
60
|
-
body.append(f"
|
|
90
|
+
body.append(f" ▸ {feature}", style="grey66")
|
|
61
91
|
if i != len(FEATURES) - 1:
|
|
62
92
|
body.append("\n")
|
|
63
93
|
|
|
64
94
|
return Panel(
|
|
65
|
-
|
|
95
|
+
Group(_header_row(), body),
|
|
66
96
|
border_style="grey50",
|
|
67
97
|
box=box.ROUNDED,
|
|
68
98
|
padding=(1, 4),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plexavo
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Open-source AWS misconfiguration scanner — runs with your own local AWS credentials, nothing sent to anyone else.
|
|
5
5
|
Author: Kavee
|
|
6
6
|
License: AGPL-3.0-or-later
|
|
@@ -127,20 +127,24 @@ scoped by a Condition block, or a CloudTrail lookup that hit its page cap on
|
|
|
127
127
|
|
|
128
128
|
## Quick start
|
|
129
129
|
|
|
130
|
-
**
|
|
131
|
-
|
|
132
|
-
or
|
|
130
|
+
The install path depends on your OS — **uv on macOS & Linux**, **pip on
|
|
131
|
+
Windows**. Either way it's a one-command install with no venv to create,
|
|
132
|
+
activate, or reactivate in every new terminal.
|
|
133
133
|
|
|
134
|
-
###
|
|
134
|
+
### macOS & Linux — uv
|
|
135
|
+
|
|
136
|
+
[uv](https://docs.astral.sh/uv/) installs Plexavo into its own isolated
|
|
137
|
+
environment automatically.
|
|
138
|
+
|
|
139
|
+
**1. Install uv** (if you don't already have it):
|
|
135
140
|
|
|
136
141
|
```bash
|
|
137
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
138
|
-
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # Windows
|
|
142
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
139
143
|
```
|
|
140
144
|
|
|
141
145
|
(Full options: [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/).)
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
**2. Install Plexavo:**
|
|
144
148
|
|
|
145
149
|
```bash
|
|
146
150
|
uv tool install plexavo
|
|
@@ -178,12 +182,60 @@ uv tool upgrade plexavo
|
|
|
178
182
|
uv tool uninstall plexavo
|
|
179
183
|
```
|
|
180
184
|
|
|
181
|
-
###
|
|
185
|
+
### Windows — pip
|
|
186
|
+
|
|
187
|
+
Install with `pip` and run the tool as `py -m plexavo`:
|
|
188
|
+
|
|
189
|
+
```powershell
|
|
190
|
+
py -m pip install --user plexavo
|
|
191
|
+
py -m plexavo
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`py -m plexavo` with no arguments opens the interactive menu, exactly
|
|
195
|
+
like the `plexavo` command does on macOS/Linux — flags are only for
|
|
196
|
+
scripting/CI.
|
|
197
|
+
|
|
198
|
+
> **Why not uv/pipx on Windows?** Both work by putting a small generated
|
|
199
|
+
> `plexavo.exe` launcher on your PATH. That launcher is unsigned, and
|
|
200
|
+
> Windows Smart App Control blocks unsigned executables it doesn't
|
|
201
|
+
> recognise — so `plexavo` can fail to start with a "can't confirm who
|
|
202
|
+
> published" message. `py -m plexavo` calls Python directly and never
|
|
203
|
+
> touches that launcher, so it always works. (If your machine doesn't
|
|
204
|
+
> enforce Smart App Control, `uv tool install plexavo` works here too.)
|
|
205
|
+
|
|
206
|
+
**AI-narrated explanations** (see [Cost](#cost)):
|
|
207
|
+
|
|
208
|
+
```powershell
|
|
209
|
+
py -m pip install --user "plexavo[ai]"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Updating or removing later:
|
|
213
|
+
|
|
214
|
+
```powershell
|
|
215
|
+
py -m pip install --user --upgrade plexavo
|
|
216
|
+
py -m pip uninstall plexavo
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Optional: type `plexavo` instead of `py -m plexavo`
|
|
220
|
+
|
|
221
|
+
Add a shortcut to your PowerShell profile once:
|
|
222
|
+
|
|
223
|
+
```powershell
|
|
224
|
+
Add-Content $PROFILE 'function plexavo { py -m plexavo @args }'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Open a new terminal and `plexavo` then works just like it does on
|
|
228
|
+
macOS/Linux.
|
|
229
|
+
|
|
230
|
+
### Run a scan
|
|
182
231
|
|
|
183
232
|
```bash
|
|
184
233
|
plexavo scan --profile my-aws-profile --report-html report.html
|
|
185
234
|
```
|
|
186
235
|
|
|
236
|
+
(On Windows without the shortcut above: `py -m plexavo scan --profile
|
|
237
|
+
my-aws-profile --report-html report.html`.)
|
|
238
|
+
|
|
187
239
|
No `--profile`? It uses your default profile / environment variables,
|
|
188
240
|
same resolution order as the AWS CLI. No AI, no API key, no cost — and
|
|
189
241
|
findings still come with free Next Step / Full Fix Detail guidance
|
|
@@ -213,19 +265,24 @@ plexavo scan --profile my-aws-profile --explain --report-html report.html --repo
|
|
|
213
265
|
|
|
214
266
|
### Alternative: pipx
|
|
215
267
|
|
|
216
|
-
Already use [pipx](https://pipx.pypa.io/)? It works exactly
|
|
217
|
-
its own isolated environment, one global command, no
|
|
268
|
+
Already use [pipx](https://pipx.pypa.io/) on macOS/Linux? It works exactly
|
|
269
|
+
the same way as uv — its own isolated environment, one global command, no
|
|
270
|
+
venv:
|
|
218
271
|
|
|
219
272
|
```bash
|
|
220
273
|
pipx install plexavo
|
|
221
274
|
pipx install "plexavo[ai]" # with AI-narrated explanations
|
|
222
275
|
```
|
|
223
276
|
|
|
277
|
+
On Windows, pipx has the same Smart App Control caveat as uv (see the
|
|
278
|
+
Windows section above) — if the `plexavo` command is blocked, run
|
|
279
|
+
`py -m plexavo` instead, or use the `pip` install.
|
|
280
|
+
|
|
224
281
|
### Alternative: pip + venv
|
|
225
282
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
283
|
+
Want full isolation and prefer to manage the environment yourself? Modern
|
|
284
|
+
Python (PEP 668) blocks a plain `pip install` outside a venv on many
|
|
285
|
+
Linux and Homebrew setups, so create one first:
|
|
229
286
|
|
|
230
287
|
```bash
|
|
231
288
|
python -m venv plexavo-env
|
|
@@ -238,7 +295,9 @@ pip install "plexavo[ai]" # with AI-narrated explanations
|
|
|
238
295
|
|
|
239
296
|
You'll need to reactivate this venv (`plexavo-env\Scripts\activate` /
|
|
240
297
|
`source plexavo-env/bin/activate`) every time you open a new terminal —
|
|
241
|
-
`deactivate` exits it without uninstalling anything.
|
|
298
|
+
`deactivate` exits it without uninstalling anything. On Windows, run
|
|
299
|
+
`python -m plexavo` if the `plexavo` command is blocked by Smart App
|
|
300
|
+
Control.
|
|
242
301
|
|
|
243
302
|
### From source (for contributing, or trying an unreleased change)
|
|
244
303
|
|
|
@@ -257,6 +316,7 @@ plexavo/
|
|
|
257
316
|
├── findings.py # Finding data model, Severity enum
|
|
258
317
|
├── scoring.py # 0-100 score from a list of Findings
|
|
259
318
|
├── cli.py # `plexavo scan ...` entry point
|
|
319
|
+
├── __main__.py # lets `python -m plexavo` run the CLI
|
|
260
320
|
├── checks/
|
|
261
321
|
│ ├── iam.py # IAM-01 to IAM-06 (privilege escalation)
|
|
262
322
|
│ ├── iam_hygiene.py # IAM-07 to IAM-14 (hygiene, cross-account trust)
|
|
@@ -2,6 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
plexavo/__init__.py
|
|
5
|
+
plexavo/__main__.py
|
|
5
6
|
plexavo/auth.py
|
|
6
7
|
plexavo/aws_profile_setup.py
|
|
7
8
|
plexavo/cli.py
|
|
@@ -34,6 +35,8 @@ plexavo/report/fonts/DejaVuSans.ttf
|
|
|
34
35
|
plexavo/report/fonts/GEIST-FONT-LICENSE.txt
|
|
35
36
|
plexavo/report/templates/report.html.j2
|
|
36
37
|
tests/test_ai_narration_offline.py
|
|
38
|
+
tests/test_auth_offline.py
|
|
39
|
+
tests/test_cli_entrypoint.py
|
|
37
40
|
tests/test_encryption_offline.py
|
|
38
41
|
tests/test_iam_hygiene_offline.py
|
|
39
42
|
tests/test_iam_offline.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "plexavo"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Open-source AWS misconfiguration scanner — runs with your own local AWS credentials, nothing sent to anyone else."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "AGPL-3.0-or-later" }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Offline tests for auth.py. No real AWS calls — boto3.Session.client is
|
|
2
|
+
monkeypatched to raise the errors AWS itself would raise, to prove
|
|
3
|
+
get_local_session converts them into a clear RuntimeError instead of
|
|
4
|
+
letting them crash the caller.
|
|
5
|
+
|
|
6
|
+
Run: python test_auth_offline.py
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import boto3
|
|
10
|
+
from botocore.exceptions import ClientError, NoCredentialsError
|
|
11
|
+
from plexavo.auth import get_local_session
|
|
12
|
+
|
|
13
|
+
failures = 0
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def assert_true(cond, msg):
|
|
17
|
+
global failures
|
|
18
|
+
status = "PASS" if cond else "FAIL"
|
|
19
|
+
print(f"[{status}] {msg}")
|
|
20
|
+
if not cond:
|
|
21
|
+
failures += 1
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _RaisingClient:
|
|
25
|
+
def __init__(self, exc):
|
|
26
|
+
self._exc = exc
|
|
27
|
+
|
|
28
|
+
def get_caller_identity(self):
|
|
29
|
+
raise self._exc
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _patch_client(monkeypatch_exc):
|
|
33
|
+
original_client = boto3.Session.client
|
|
34
|
+
|
|
35
|
+
def fake_client(self, service_name, *args, **kwargs):
|
|
36
|
+
if service_name == "sts":
|
|
37
|
+
return _RaisingClient(monkeypatch_exc)
|
|
38
|
+
return original_client(self, service_name, *args, **kwargs)
|
|
39
|
+
|
|
40
|
+
return original_client, fake_client
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# --- InvalidClientTokenId (deactivated/deleted access key) surfaces as a clear RuntimeError ---
|
|
44
|
+
original_client, fake_client = _patch_client(
|
|
45
|
+
ClientError(
|
|
46
|
+
{"Error": {"Code": "InvalidClientTokenId", "Message": "The security token included in the request is invalid."}},
|
|
47
|
+
"GetCallerIdentity",
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
boto3.Session.region_name = property(lambda self: "us-east-1")
|
|
51
|
+
boto3.Session.client = fake_client
|
|
52
|
+
try:
|
|
53
|
+
raised = None
|
|
54
|
+
try:
|
|
55
|
+
# profile_name=None: boto3.Session() itself always succeeds
|
|
56
|
+
# regardless of what's in this machine's real ~/.aws config —
|
|
57
|
+
# only the (monkeypatched) sts call should fail.
|
|
58
|
+
get_local_session(profile_name=None)
|
|
59
|
+
except RuntimeError as e:
|
|
60
|
+
raised = e
|
|
61
|
+
except Exception as e:
|
|
62
|
+
raised = e
|
|
63
|
+
|
|
64
|
+
assert_true(isinstance(raised, RuntimeError), "ClientError (InvalidClientTokenId) is converted to RuntimeError, not left to crash")
|
|
65
|
+
assert_true(raised is not None and "InvalidClientTokenId" in str(raised), "RuntimeError message names the AWS error code")
|
|
66
|
+
finally:
|
|
67
|
+
boto3.Session.client = original_client
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# --- NoCredentialsError still produces the pre-existing clear message ---
|
|
71
|
+
original_client, fake_client = _patch_client(NoCredentialsError())
|
|
72
|
+
boto3.Session.client = fake_client
|
|
73
|
+
try:
|
|
74
|
+
raised = None
|
|
75
|
+
try:
|
|
76
|
+
get_local_session(profile_name=None)
|
|
77
|
+
except RuntimeError as e:
|
|
78
|
+
raised = e
|
|
79
|
+
except Exception as e:
|
|
80
|
+
raised = e
|
|
81
|
+
|
|
82
|
+
assert_true(isinstance(raised, RuntimeError), "NoCredentialsError still converts to RuntimeError (regression check)")
|
|
83
|
+
assert_true(raised is not None and "No AWS credentials found" in str(raised), "NoCredentialsError message unchanged")
|
|
84
|
+
finally:
|
|
85
|
+
boto3.Session.client = original_client
|
|
86
|
+
del boto3.Session.region_name
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
print(f"\n{'ALL PASSED' if failures == 0 else f'{failures} FAILURE(S)'}")
|
|
90
|
+
import sys
|
|
91
|
+
sys.exit(1 if failures else 0)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Checks that `python -m plexavo` works and is equivalent to the installed
|
|
2
|
+
`plexavo` command / `python -m plexavo.cli`.
|
|
3
|
+
|
|
4
|
+
This is the Windows install path (README Quick start): install with pip, run
|
|
5
|
+
via `py -m plexavo` instead of the Smart-App-Control-blocked `plexavo.exe`
|
|
6
|
+
launcher. If the `plexavo/__main__.py` shim regresses, that path breaks.
|
|
7
|
+
|
|
8
|
+
Run: python test_cli_entrypoint.py
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
failures = 0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def assert_true(cond, msg):
|
|
18
|
+
global failures
|
|
19
|
+
status = "PASS" if cond else "FAIL"
|
|
20
|
+
print(f"[{status}] {msg}")
|
|
21
|
+
if not cond:
|
|
22
|
+
failures += 1
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run(*args):
|
|
26
|
+
return subprocess.run(
|
|
27
|
+
[sys.executable, *args],
|
|
28
|
+
capture_output=True,
|
|
29
|
+
text=True,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
print("=== python -m plexavo --version ===")
|
|
34
|
+
mod = run("-m", "plexavo", "--version")
|
|
35
|
+
assert_true(mod.returncode == 0, f"exits 0 (got {mod.returncode}, stderr: {mod.stderr!r})")
|
|
36
|
+
assert_true("plexavo" in (mod.stdout + mod.stderr).lower(), "prints the version string")
|
|
37
|
+
|
|
38
|
+
print("\n=== equivalent to python -m plexavo.cli ===")
|
|
39
|
+
cli = run("-m", "plexavo.cli", "--version")
|
|
40
|
+
assert_true(mod.stdout == cli.stdout, f"same --version output ({mod.stdout!r} vs {cli.stdout!r})")
|
|
41
|
+
|
|
42
|
+
print("\n=== python -m plexavo --help mentions the scan subcommand ===")
|
|
43
|
+
helptext = run("-m", "plexavo", "--help")
|
|
44
|
+
assert_true(helptext.returncode == 0, f"--help exits 0 (got {helptext.returncode})")
|
|
45
|
+
assert_true("scan" in helptext.stdout, "help lists the scan subcommand")
|
|
46
|
+
assert_true("usage: plexavo" in helptext.stdout,
|
|
47
|
+
"usage line shows 'plexavo', not '__main__.py'")
|
|
48
|
+
|
|
49
|
+
print(f"\n{'ALL PASSED' if failures == 0 else f'{failures} FAILURE(S)'}")
|
|
50
|
+
sys.exit(1 if failures else 0)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|