package-alert 0.1.0__py3-none-any.whl
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.
- package_alert-0.1.0.dist-info/METADATA +433 -0
- package_alert-0.1.0.dist-info/RECORD +47 -0
- package_alert-0.1.0.dist-info/WHEEL +4 -0
- package_alert-0.1.0.dist-info/entry_points.txt +2 -0
- package_alert-0.1.0.dist-info/licenses/LICENSE +194 -0
- packagealert/__init__.py +1 -0
- packagealert/alerts/__init__.py +1 -0
- packagealert/alerts/desktop.py +51 -0
- packagealert/alerts/terminal.py +65 -0
- packagealert/analyzers/__init__.py +1 -0
- packagealert/analyzers/risk.py +99 -0
- packagealert/cli/__init__.py +1 -0
- packagealert/cli/app.py +898 -0
- packagealert/cli/status.py +357 -0
- packagealert/config.py +147 -0
- packagealert/daemon.py +232 -0
- packagealert/heuristics/__init__.py +1 -0
- packagealert/heuristics/base.py +12 -0
- packagealert/heuristics/npm.py +102 -0
- packagealert/heuristics/python.py +85 -0
- packagealert/heuristics/typosquat.py +80 -0
- packagealert/logging_setup.py +49 -0
- packagealert/models/__init__.py +1 -0
- packagealert/models/advisories.py +30 -0
- packagealert/models/events.py +27 -0
- packagealert/models/risk.py +27 -0
- packagealert/monitors/__init__.py +1 -0
- packagealert/monitors/base.py +31 -0
- packagealert/monitors/cache.py +160 -0
- packagealert/monitors/process.py +236 -0
- packagealert/osv/__init__.py +1 -0
- packagealert/osv/cache.py +77 -0
- packagealert/osv/client.py +142 -0
- packagealert/osv/popularity.py +77 -0
- packagealert/parsers/__init__.py +1 -0
- packagealert/parsers/lockfiles.py +304 -0
- packagealert/parsers/npm.py +68 -0
- packagealert/parsers/process_args.py +360 -0
- packagealert/parsers/wheel.py +58 -0
- packagealert/sandbox/__init__.py +0 -0
- packagealert/sandbox/bwrap.py +69 -0
- packagealert/sandbox/runner.py +992 -0
- packagealert/scheduler/__init__.py +0 -0
- packagealert/scheduler/db.py +267 -0
- packagealert/scheduler/runner.py +228 -0
- packagealert/storage/__init__.py +1 -0
- packagealert/storage/db.py +111 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: package-alert
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Real-time security monitor for Python, Node.js, and PHP package installations
|
|
5
|
+
Project-URL: Homepage, https://github.com/alslater/package-alert
|
|
6
|
+
Project-URL: Repository, https://github.com/alslater/package-alert
|
|
7
|
+
Author-email: Al Slater <al.slater@essiell.com>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: composer,malware,npm,osv,pip,sandbox,security,supply-chain
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Security
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: aiosqlite>=0.20
|
|
22
|
+
Requires-Dist: httpx>=0.27
|
|
23
|
+
Requires-Dist: levenshtein>=0.25
|
|
24
|
+
Requires-Dist: psutil>=6
|
|
25
|
+
Requires-Dist: pydantic>=2.7
|
|
26
|
+
Requires-Dist: rich>=13
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Requires-Dist: watchdog>=4
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: hatch; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-mock>=3; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
34
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# package-alert
|
|
38
|
+
|
|
39
|
+
**package-alert** monitors Python, Node.js, and PHP package installations in real time and alerts developers when a malicious or suspicious package is detected. It can also run package manager commands inside an isolated bubblewrap sandbox with pre-flight and post-install OSV checks, scan project lock files or installed environments on demand, and schedule automatic daily or weekly scans across multiple projects.
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Real-time process monitoring** — detects `pip install`, `uv add`, `uv sync`, `pipenv install`, `npm install`, `composer require`, and more as they happen
|
|
44
|
+
- **Lock file scanning** — after a lock-file-based install finishes, reads the lock file for exact package versions and scans all of them in a single OSV batch call
|
|
45
|
+
- **Cache monitoring** — watches pip/uv/npm cache directories for newly downloaded packages
|
|
46
|
+
- **OSV.dev integration** — checks every package against the [Open Source Vulnerabilities](https://osv.dev) database
|
|
47
|
+
- **Heuristic risk scoring** — detects suspicious packages even without a known advisory
|
|
48
|
+
- **Typosquatting detection** — flags packages that closely resemble popular libraries (Levenshtein distance)
|
|
49
|
+
- **Popularity signal** — queries deps.dev to flag packages with very few versions or dependents
|
|
50
|
+
- **Low latency alerts** — Rich terminal panel + `notify-send` desktop notifications
|
|
51
|
+
- **Alert history** — all alerts persisted in SQLite with package name, version, advisory, and project path
|
|
52
|
+
- **Sandboxed installs** — `package-alert run` wraps any package manager command in a bubblewrap sandbox with pre-flight and post-install OSV checks
|
|
53
|
+
|
|
54
|
+
## Supported Ecosystems
|
|
55
|
+
|
|
56
|
+
| Ecosystem | Package managers monitored | Lock files scanned |
|
|
57
|
+
|-----------|---------------------------|-------------------|
|
|
58
|
+
| PyPI | `pip`, `python -m pip`, `uv add`, `uv sync`, `uv lock`, `pipenv install` | `uv.lock`, `Pipfile.lock`, `requirements.txt` |
|
|
59
|
+
| npm | `npm install`, `npm add`, `npm ci` | `package-lock.json` |
|
|
60
|
+
| Packagist | `composer install`, `composer update`, `composer require`, `php composer.phar …` | `composer.lock`, `composer.json` |
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
**Recommended — pipx (isolated environment, `package-alert` available system-wide):**
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pipx install package-alert
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Development install:**
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pipx install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Start the background daemon
|
|
80
|
+
package-alert daemon
|
|
81
|
+
|
|
82
|
+
# Check daemon status
|
|
83
|
+
package-alert status
|
|
84
|
+
|
|
85
|
+
# Run a package manager command in a sandbox
|
|
86
|
+
package-alert run uv sync
|
|
87
|
+
package-alert run npm install
|
|
88
|
+
|
|
89
|
+
# Scan the current project's lock files for vulnerabilities
|
|
90
|
+
package-alert scan-project
|
|
91
|
+
|
|
92
|
+
# Scan an explicit requirements file (e.g. a pinned CI lockfile)
|
|
93
|
+
package-alert scan-project -r requirements-lock.txt
|
|
94
|
+
|
|
95
|
+
# Query a specific package
|
|
96
|
+
package-alert query requests 2.31.0
|
|
97
|
+
|
|
98
|
+
# View recent alerts
|
|
99
|
+
package-alert alerts
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Commands
|
|
103
|
+
|
|
104
|
+
### Global options
|
|
105
|
+
|
|
106
|
+
| Option | Description |
|
|
107
|
+
|--------|-------------|
|
|
108
|
+
| `--verbose` / `-v` | Print log output to the console. Without this flag log output is written only to the configured log file. |
|
|
109
|
+
| `--config` / `-c` | Path to a TOML config file (overrides the default `~/.config/package-alert/config.toml`). |
|
|
110
|
+
|
|
111
|
+
### `daemon`
|
|
112
|
+
|
|
113
|
+
Start the monitoring daemon. Only one instance may run at a time (enforced via a PID file at `~/.local/share/package-alert/daemon.pid`).
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
package-alert daemon [--config PATH]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The daemon:
|
|
120
|
+
1. Polls running processes every second for package manager invocations
|
|
121
|
+
2. Watches pip/uv/npm cache directories with inotify for newly downloaded wheels/tarballs
|
|
122
|
+
3. Dynamically registers site-packages directories when a venv install is detected
|
|
123
|
+
4. Waits for lock-file-based installs (npm, uv sync/lock, pipenv, composer) to finish, then reads the lock file and sends all packages to OSV in a single batch call
|
|
124
|
+
5. Checks each package against OSV; fires alerts for malicious packages or those exceeding the heuristic risk threshold
|
|
125
|
+
|
|
126
|
+
### `run`
|
|
127
|
+
|
|
128
|
+
Run a package manager command inside a [bubblewrap](https://github.com/containers/bubblewrap) sandbox.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
package-alert run [--no-network] <command> [args...]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Examples:**
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
package-alert run uv sync
|
|
138
|
+
package-alert run uv add httpx
|
|
139
|
+
package-alert run pip install requests flask==3.0.0
|
|
140
|
+
package-alert run npm install
|
|
141
|
+
package-alert run npm install lodash@4.17.21
|
|
142
|
+
package-alert run composer install
|
|
143
|
+
package-alert run --no-network uv sync # fully offline; uv cache must be warm
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**What it does:**
|
|
147
|
+
|
|
148
|
+
1. **Pre-flight check** — identifies what will be installed (from the command arguments or the project lock file) and batch-queries OSV before anything runs. Blocks immediately if a known-malicious package is found.
|
|
149
|
+
2. **Sandboxed execution** — runs the command inside a bubblewrap namespace with layered filesystem isolation (see below). Network access is **allowed by default** so package managers can reach their registries; use `--no-network` only when all packages are already cached locally.
|
|
150
|
+
3. **Post-install scan** — diffs the install targets against a pre-run snapshot, identifies new packages by their metadata files (`.dist-info`, `package.json`, `composer.json`), and runs another OSV check on everything that appeared.
|
|
151
|
+
|
|
152
|
+
| Option | Description |
|
|
153
|
+
|--------|-------------|
|
|
154
|
+
| `--no-network` | Block all outbound network inside the sandbox. Use only when all packages are already in the local cache. |
|
|
155
|
+
| `--env VAR` | Pass an additional environment variable through into the sandbox. Repeatable: `--env MY_TOKEN --env CUSTOM_URL`. |
|
|
156
|
+
| `--expose-ssh-keys` | Expose `~/.ssh` read-only inside the sandbox. Required when installing packages with `git+ssh://` or scp-style (`git@host:org/repo`) VCS dependencies. package-alert detects these automatically and suggests the flag if it is not passed. |
|
|
157
|
+
| `--config PATH` | Path to config TOML file. |
|
|
158
|
+
|
|
159
|
+
**Filesystem isolation:**
|
|
160
|
+
|
|
161
|
+
The sandbox uses a layered mount strategy to prevent install-time scripts from reading credentials or secrets outside the project:
|
|
162
|
+
|
|
163
|
+
| Layer | What happens |
|
|
164
|
+
|-------|-------------|
|
|
165
|
+
| `/` read-only | The entire filesystem is mounted read-only — install scripts cannot modify system files or other projects. |
|
|
166
|
+
| `$HOME` hidden | A fresh empty tmpfs is overlaid on the home directory, hiding `~/.ssh`, `~/.aws`, `~/.gnupg`, `.env` files in sibling projects, and any other secrets stored there. |
|
|
167
|
+
| Safe home paths re-exposed | A curated allowlist of home subdirectories is re-mounted read-only inside the tmpfs so package managers can function normally (see table below). |
|
|
168
|
+
| Install targets writable | The project directory, site-packages/`node_modules`/`vendor`, and package manager caches are bound writable on top of the above. |
|
|
169
|
+
|
|
170
|
+
Paths re-exposed inside the home tmpfs (read-only):
|
|
171
|
+
|
|
172
|
+
| Path | Purpose |
|
|
173
|
+
|------|---------|
|
|
174
|
+
| `$PYENV_ROOT` (`~/.pyenv`) | pyenv-managed Python installations |
|
|
175
|
+
| `$NVM_DIR` (`~/.nvm`) | nvm-managed Node.js installations |
|
|
176
|
+
| `~/.local/bin` | User-local binaries (uv, pip-installed scripts, etc.) |
|
|
177
|
+
| `~/.local/share/uv` | uv-managed Python installations and tool environments |
|
|
178
|
+
| `~/.local/pipx` | pipx-managed tool environments (shebangs in `~/.local/bin` may point here) |
|
|
179
|
+
| `~/.config/pip`, `~/.pip` | pip configuration (index URLs, proxy settings) |
|
|
180
|
+
| `~/.config/uv` | uv configuration |
|
|
181
|
+
| `~/.npmrc` | npm registry and auth configuration |
|
|
182
|
+
| `~/.cache/pip`, `~/.cache/uv`, `~/.npm` | Package manager caches (writable) |
|
|
183
|
+
| `~/.config/composer` | Composer home (writable, when present) |
|
|
184
|
+
|
|
185
|
+
Paths that are **not** accessible inside the sandbox by default: `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.config/gcloud`, `~/.netrc`, `~/.git-credentials`, and everything else in `$HOME` not listed above. Pass `--expose-ssh-keys` to re-expose `~/.ssh` read-only when SSH-authenticated VCS dependencies are needed.
|
|
186
|
+
|
|
187
|
+
**Environment isolation:**
|
|
188
|
+
|
|
189
|
+
The sandbox process also starts with a stripped environment. A curated set of variables is forwarded (PATH, HOME, locale, proxy settings, registry URLs for pip/uv/npm/composer, SSL certificates, pyenv/nvm locations). Variables not in this allowlist are removed. Use `--env VAR` on the command line or `sandbox.extra_env` in the config file to forward additional variables.
|
|
190
|
+
|
|
191
|
+
**Requirements:** `bwrap` (bubblewrap) must be installed.
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Ubuntu/Debian
|
|
195
|
+
sudo apt install bubblewrap
|
|
196
|
+
|
|
197
|
+
# Fedora/RHEL
|
|
198
|
+
sudo dnf install bubblewrap
|
|
199
|
+
|
|
200
|
+
# Arch
|
|
201
|
+
sudo pacman -S bubblewrap
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Virtual environment detection:** for Python commands, package-alert automatically detects the target site-packages directory by checking (in order) the executable path in the command, `VIRTUAL_ENV` (pip/pipenv only — uv always uses the project-local `.venv`), and `.venv`/`venv` directories in the current working directory.
|
|
205
|
+
|
|
206
|
+
### `status`
|
|
207
|
+
|
|
208
|
+
Show the current state of the daemon and related paths.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
package-alert status [--json] [--config PATH]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Displays:
|
|
215
|
+
|
|
216
|
+
- Daemon running/stopped, PID, uptime, and whether it was started by systemd
|
|
217
|
+
- Config file path in use
|
|
218
|
+
- Daemon log file path and whether it exists
|
|
219
|
+
- CLI log file path and whether it exists
|
|
220
|
+
|
|
221
|
+
Use `--json` for machine-readable output.
|
|
222
|
+
|
|
223
|
+
### `scan-project`
|
|
224
|
+
|
|
225
|
+
Scan a project directory for vulnerable or malicious packages.
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
package-alert scan-project [PATH] [OPTIONS]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
| Option | Default | Description |
|
|
232
|
+
|--------|---------|-------------|
|
|
233
|
+
| `PATH` | `.` | Project directory |
|
|
234
|
+
| `--scan-unpinned` | off | Also query OSV for unpinned/range-constrained dependencies |
|
|
235
|
+
| `--scan-installed` | off | Scan `venv/.venv` site-packages or `node_modules` instead of lock files |
|
|
236
|
+
| `--requirements` / `-r` | — | Explicit requirements file to scan instead of auto-detecting lock files (mutually exclusive with `--scan-installed`) |
|
|
237
|
+
| `--details` / `-d` | off | Show full advisory details and URL |
|
|
238
|
+
| `--format` / `-f` | `text` | Output format: `text`, `json`, `html`, `browser` |
|
|
239
|
+
|
|
240
|
+
**Formats:**
|
|
241
|
+
|
|
242
|
+
- `text` — colour-coded terminal output; severity badge on the advisory line (`[HIGH] GHSA-…`)
|
|
243
|
+
- `json` — machine-readable JSON with all findings, unpinned packages, and sources
|
|
244
|
+
- `html` — self-contained HTML report printed to stdout
|
|
245
|
+
- `browser` — writes HTML to `/tmp/package-alert-*.html` and opens it in the default browser
|
|
246
|
+
|
|
247
|
+
`--requirements` accepts a path relative to `PATH` (the project directory) or an absolute path. Nested `-r`/`--requirement` includes within the file are followed recursively. `--requirements` and `--scan-installed` are mutually exclusive.
|
|
248
|
+
|
|
249
|
+
**Auto-detected lock files (in order of precedence):**
|
|
250
|
+
|
|
251
|
+
- `package-lock.json` → npm
|
|
252
|
+
- `uv.lock` → PyPI (uv)
|
|
253
|
+
- `Pipfile.lock` → PyPI (pipenv)
|
|
254
|
+
- `requirements.txt` / `requirements/base.txt` / `requirements/prod.txt` → PyPI (only when no uv/pipenv lock found)
|
|
255
|
+
- `composer.lock` → Packagist
|
|
256
|
+
- `composer.json` (fallback when no lock file) → Packagist
|
|
257
|
+
|
|
258
|
+
### `scan-cache`
|
|
259
|
+
|
|
260
|
+
Scan pip and uv cache directories for wheels that have known malicious advisories.
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
package-alert scan-cache [--config PATH]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### `query`
|
|
267
|
+
|
|
268
|
+
Query OSV for a specific package, with full advisory details.
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
package-alert query PACKAGE [VERSION] [--ecosystem pypi|npm] [--config PATH]
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### `alerts`
|
|
275
|
+
|
|
276
|
+
Show recent alerts stored in the database.
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
package-alert alerts [--limit N] [--config PATH]
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Displays a table with: package name, ecosystem, version, advisory ID or risk score, project path, and timestamp.
|
|
283
|
+
|
|
284
|
+
### `clear-cache`
|
|
285
|
+
|
|
286
|
+
Clear the OSV query cache.
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
package-alert clear-cache [--ecosystem pypi|npm] [--config PATH]
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Omit `--ecosystem` to clear all ecosystems.
|
|
293
|
+
|
|
294
|
+
### `config-show`
|
|
295
|
+
|
|
296
|
+
Print the resolved configuration as JSON (useful for verifying config file is being read).
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
package-alert config-show [--config PATH]
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Scheduled Scans
|
|
303
|
+
|
|
304
|
+
Register projects for automatic daily or weekly scans run by the daemon. Each path can be registered for multiple scan types independently.
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Register the current project for daily lock-file scans
|
|
308
|
+
package-alert schedule add --daily
|
|
309
|
+
|
|
310
|
+
# Also register for weekly installed-packages scans (both coexist independently)
|
|
311
|
+
package-alert schedule add --weekly --installed
|
|
312
|
+
|
|
313
|
+
# Register a specific path
|
|
314
|
+
package-alert schedule add /path/to/project --daily
|
|
315
|
+
|
|
316
|
+
# List all registered projects (shows all path/scan_type pairs)
|
|
317
|
+
package-alert schedule list
|
|
318
|
+
|
|
319
|
+
# Remove only the installed-packages scan entry
|
|
320
|
+
package-alert schedule remove --installed
|
|
321
|
+
|
|
322
|
+
# Remove all scan entries for the current project
|
|
323
|
+
package-alert schedule remove
|
|
324
|
+
|
|
325
|
+
# List completed scans for the current project (newest first)
|
|
326
|
+
package-alert scans list
|
|
327
|
+
package-alert scans list /path/to/project --limit 10
|
|
328
|
+
|
|
329
|
+
# Show findings from a specific scan
|
|
330
|
+
package-alert scans show 42
|
|
331
|
+
package-alert scans show 42 --format json
|
|
332
|
+
package-alert scans show 42 --format html
|
|
333
|
+
package-alert scans show 42 --format browser
|
|
334
|
+
package-alert scans show 42 --details
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Configuration
|
|
338
|
+
|
|
339
|
+
Config is loaded from `~/.config/package-alert/config.toml` automatically if it exists. Override with `--config PATH` on any command.
|
|
340
|
+
|
|
341
|
+
```toml
|
|
342
|
+
# Logging for the long-running daemon process.
|
|
343
|
+
[log]
|
|
344
|
+
level = "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
345
|
+
file = "~/.local/share/package-alert/daemon.log" # set file = "" to disable file logging
|
|
346
|
+
# max_bytes = 10485760 # 10 MB per file before rotation
|
|
347
|
+
# backup_count = 3
|
|
348
|
+
|
|
349
|
+
# Logging for short-lived CLI commands (scan-project, query, alerts, etc.).
|
|
350
|
+
[cli_log]
|
|
351
|
+
level = "INFO"
|
|
352
|
+
file = "~/.local/share/package-alert/cli.log" # set file = "" to disable file logging
|
|
353
|
+
|
|
354
|
+
[watch]
|
|
355
|
+
enable_cache_monitoring = true
|
|
356
|
+
enable_process_monitoring = true
|
|
357
|
+
pip_cache_dir = "~/.cache/pip"
|
|
358
|
+
uv_cache_dir = "~/.cache/uv"
|
|
359
|
+
npm_cache_dir = "~/.npm/_cacache"
|
|
360
|
+
site_packages_dirs = [] # extra site-packages to watch
|
|
361
|
+
process_poll_interval_seconds = 1.0
|
|
362
|
+
|
|
363
|
+
[osv]
|
|
364
|
+
base_url = "https://api.osv.dev/v1"
|
|
365
|
+
cache_ttl_hours = 24
|
|
366
|
+
timeout_seconds = 10.0
|
|
367
|
+
max_retries = 3
|
|
368
|
+
|
|
369
|
+
[alerts]
|
|
370
|
+
desktop_notifications = true
|
|
371
|
+
terminal_notifications = true
|
|
372
|
+
min_severity_for_desktop = "MEDIUM"
|
|
373
|
+
|
|
374
|
+
[heuristics]
|
|
375
|
+
enabled = true
|
|
376
|
+
warning_threshold = 40
|
|
377
|
+
critical_threshold = 70
|
|
378
|
+
|
|
379
|
+
[sandbox]
|
|
380
|
+
# Additional environment variable names to forward into the sandbox beyond
|
|
381
|
+
# the built-in allowlist (PATH, HOME, proxy vars, registry URLs, etc.).
|
|
382
|
+
extra_env = []
|
|
383
|
+
# Example: extra_env = ["MY_PRIVATE_REGISTRY_TOKEN", "CUSTOM_CERT_PATH"]
|
|
384
|
+
|
|
385
|
+
# Additional paths to mount as empty tmpfs inside the sandbox.
|
|
386
|
+
# Use this on systems where other root-owned paths cause tool failures inside
|
|
387
|
+
# bwrap's user namespace (e.g. SSH proxy config files owned by root).
|
|
388
|
+
extra_tmpfs = []
|
|
389
|
+
# Example: extra_tmpfs = ["/etc/ssh/other_config.d"]
|
|
390
|
+
|
|
391
|
+
[scheduler]
|
|
392
|
+
enabled = true
|
|
393
|
+
daily_hour = 2 # hour of day (0–23) to run daily scans
|
|
394
|
+
weekly_day = 6 # day of week to run weekly scans (0=Mon … 6=Sun)
|
|
395
|
+
weekly_hour = 2 # hour of day to run weekly scans
|
|
396
|
+
max_scan_history = 5 # completed scan records to keep per project per scan type
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Scan types:**
|
|
400
|
+
|
|
401
|
+
- `project` (default) — scans lock files (`requirements.txt`, `uv.lock`, `package-lock.json`, `composer.lock`). Reproducible; works offline with cached OSV results.
|
|
402
|
+
- `installed` — enumerates packages actually installed in the project's virtual environment (`pip list`, `npm ls`, `composer show`). Catches drift between lock file and real environment.
|
|
403
|
+
|
|
404
|
+
## Data Storage
|
|
405
|
+
|
|
406
|
+
All persistent data lives in `~/.local/share/package-alert/`:
|
|
407
|
+
|
|
408
|
+
| File | Purpose |
|
|
409
|
+
|------|---------|
|
|
410
|
+
| `package-alert.db` | SQLite database: OSV cache, alert history, popularity cache |
|
|
411
|
+
| `daemon.log` | Rotating daemon log file (10 MB × 3 backups) |
|
|
412
|
+
| `cli.log` | Rotating CLI command log file (10 MB × 3 backups) |
|
|
413
|
+
| `daemon.pid` | PID file used to prevent duplicate daemon instances |
|
|
414
|
+
|
|
415
|
+
## systemd (Linux)
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
mkdir -p ~/.config/systemd/user
|
|
419
|
+
cp package-alert.service ~/.config/systemd/user/
|
|
420
|
+
systemctl --user enable --now package-alert
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Architecture
|
|
424
|
+
|
|
425
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md).
|
|
426
|
+
|
|
427
|
+
## Security
|
|
428
|
+
|
|
429
|
+
See [THREAT_MODEL.md](THREAT_MODEL.md).
|
|
430
|
+
|
|
431
|
+
## Roadmap
|
|
432
|
+
|
|
433
|
+
See [ROADMAP.md](ROADMAP.md).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
packagealert/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
|
+
packagealert/config.py,sha256=i8fVuq1ksArMYt3p-fkmjMF64-gYghCXgdffeKVQNko,4640
|
|
3
|
+
packagealert/daemon.py,sha256=xMKVfvl5_SG9G0QyETOI2IABLf48WrHI2Lw6ondL3KA,8853
|
|
4
|
+
packagealert/logging_setup.py,sha256=jd_yd161jDlw8k2dJcjrLt2x8lki-LgapXqlIyMT5E4,1445
|
|
5
|
+
packagealert/alerts/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
|
+
packagealert/alerts/desktop.py,sha256=dwfRltaRZxAFq1NNdNTlj3-E3Y4ROi-w1yN09FZwpzg,1723
|
|
7
|
+
packagealert/alerts/terminal.py,sha256=YglcEe1XdcAVNOpoqw2nISufu-txgXIMOaDvtcP5SGM,2317
|
|
8
|
+
packagealert/analyzers/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
+
packagealert/analyzers/risk.py,sha256=K4mfUdPiB4oZ_DEYpapuqK9C0nWRGlI-e9GEbmvQTX0,4062
|
|
10
|
+
packagealert/cli/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
11
|
+
packagealert/cli/app.py,sha256=bpWQRdXBFcrAm1kjAdq12NjCORxKCbuuwXe7LINuf_s,32747
|
|
12
|
+
packagealert/cli/status.py,sha256=i7EFN7hqvkINVOi1ROFBAZWMfohtp-Li-NUS-GNMWAc,13685
|
|
13
|
+
packagealert/heuristics/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
14
|
+
packagealert/heuristics/base.py,sha256=iAku5fzHSYNzpg5S7Y5JZ4LsifXjoE6I7mLWbBA9730,278
|
|
15
|
+
packagealert/heuristics/npm.py,sha256=9I1yh9Str37SaYVURU8j0E1-5SOxuTZQ1pw8e2iHlN0,3431
|
|
16
|
+
packagealert/heuristics/python.py,sha256=f0RgI_LfHkloS4Zf4ElduYsLnNmKDsP-ePNrkOyIuMg,2908
|
|
17
|
+
packagealert/heuristics/typosquat.py,sha256=bbAbezj4n_Tvwfgu_hAJAlgZz0IOH43z1hFLAS6zThE,3198
|
|
18
|
+
packagealert/models/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
19
|
+
packagealert/models/advisories.py,sha256=WdeIu4_h73thQzJm_9sb1vbAZqiJ6Cn2KsDg79fI0Qo,718
|
|
20
|
+
packagealert/models/events.py,sha256=84vJHF9_-emI8yjK3PuhBVaHH2bV77RDVGYov5EGFhE,773
|
|
21
|
+
packagealert/models/risk.py,sha256=Yp_2zkL_xqrNYMmTZwCGlKfL6WBesuG8XBBuKLkDeww,541
|
|
22
|
+
packagealert/monitors/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
23
|
+
packagealert/monitors/base.py,sha256=-UW-zgMts-AFX-okaoHKyt_22Dg6_6DdZsWnJPc1iwg,924
|
|
24
|
+
packagealert/monitors/cache.py,sha256=KlrmQeB47YLgyK5wm8ptAAY7ZB9Zyua1v4HzxTw9IbI,5717
|
|
25
|
+
packagealert/monitors/process.py,sha256=Yn5AtUX5Wg-UKr2GstwiFIYjqoZzrx3B6uqMIXXBrCQ,9044
|
|
26
|
+
packagealert/osv/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
27
|
+
packagealert/osv/cache.py,sha256=Ia2b7CGB0xZ0p3g61cfbln6zs3_3mbzuDG9APAd-yhU,2745
|
|
28
|
+
packagealert/osv/client.py,sha256=d1CJtWDzRbj74BXGE5_B6oBpTGmqBux-8W6yUFMMm6I,5806
|
|
29
|
+
packagealert/osv/popularity.py,sha256=EUbu1rahBrACEmUGTNgcKzdFpfqYadQuuQrTMbEcogk,2656
|
|
30
|
+
packagealert/parsers/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
31
|
+
packagealert/parsers/lockfiles.py,sha256=0lYZvvxA2DRqDcp-gootqf9izgH-lwsHgJnVzHjBAS8,10485
|
|
32
|
+
packagealert/parsers/npm.py,sha256=MQQ7HcP7nsfIIL3qHtJWWU-NiW6Ffq9M87dMcmyk4DQ,2250
|
|
33
|
+
packagealert/parsers/process_args.py,sha256=9eFGyNxwyOD7_1h1N3xlde4O73x0L3GSzKk2JNsk2Fc,14111
|
|
34
|
+
packagealert/parsers/wheel.py,sha256=10MwkO8Bfxqimoekf_ZnPMwdJHwXSUPnyFg-Fr1P1sk,1790
|
|
35
|
+
packagealert/sandbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
packagealert/sandbox/bwrap.py,sha256=TVVkvu0cX6rNpmgGG6YXDfyxDdjrvE7_6CNpHp4_VPw,2754
|
|
37
|
+
packagealert/sandbox/runner.py,sha256=C1k7UVeEL29T-yW0kYrNfrqcF0mdV-DHvdBhDPEi2ng,40718
|
|
38
|
+
packagealert/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
packagealert/scheduler/db.py,sha256=M0htE8pzIeB8424bCnlPeS4y1NJ8TLUi-25J2QZB4tQ,8540
|
|
40
|
+
packagealert/scheduler/runner.py,sha256=_pYJMvoeq7w3khaWX2n79NhXZArwJZdMgT9FsRmdSTM,8819
|
|
41
|
+
packagealert/storage/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
42
|
+
packagealert/storage/db.py,sha256=kHMwMzIrqFFkV3AGfdUDzHIvi7KE3sWbUDk3Q-JcgW8,3714
|
|
43
|
+
package_alert-0.1.0.dist-info/METADATA,sha256=vfmnX1IAuTBTPx9i3z3Zd9HWCRSP9H2ogtSy5tAswYk,17361
|
|
44
|
+
package_alert-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
45
|
+
package_alert-0.1.0.dist-info/entry_points.txt,sha256=WVyua0xmy_uXc8030vDHnx9L-HYm38oXlG-CXYJnDMo,60
|
|
46
|
+
package_alert-0.1.0.dist-info/licenses/LICENSE,sha256=XTZ0pVGuaqGagC9i75KwOcpCn7bQh9DWwkKH10epLzo,10882
|
|
47
|
+
package_alert-0.1.0.dist-info/RECORD,,
|