Skip to content

Commit 3d83369

Browse files
author
Lee Chagolla-Christensen
committed
Python 3.13, linting/quality, and agent reliability
- Raise requires-python to >=3.13 across all projects/libs; set ruff target to py313 - Fix bare excepts, unused variables/imports, Optional→union syntax, and import ordering - Refactor chatbot MCP server to async subprocess with health checks and graceful shutdown - Simplify file-enrichment prod Dockerfile to use uv sync with UV_PROJECT_ENVIRONMENT - Fix module_loader to use fully qualified analyzer module name - Make YARA _analyze_yara async and fix rule_manager access in workflow - Enable LiteLLM healthcheck and add agents→litellm dependency - Add PE analyzer tests with PyInstaller fixture; - Update PDF tests for nested metadata - Suppress third-party DeprecationWarnings, use async-lru for LLM status cache - Add PHOENIX_ENABLED validation to nemesis-ctl.sh; remove debug pprint in agents - Add Claude Code managing-packages skill, update CLAUDE.md guidance
1 parent 8cd2835 commit 3d83369

70 files changed

Lines changed: 1937 additions & 3873 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
name: managing-packages
3+
description: Manages Python package dependencies. Use when adding, upgrading, removing, or syncing Python/pypi packages in projects or libs.
4+
---
5+
6+
# Managing Packages with uv
7+
8+
## Commands
9+
10+
All commands must be run from the specific project/library directory.
11+
12+
### Add a package
13+
14+
```bash
15+
cd /home/itadmin/code/Nemesis/{path} && uv add {package}
16+
```
17+
18+
### Add dev dependency
19+
20+
```bash
21+
cd /home/itadmin/code/Nemesis/{path} && uv add --dev {package}
22+
```
23+
24+
### Upgrade a package
25+
26+
```bash
27+
cd /home/itadmin/code/Nemesis/{path} && uv add {package}@latest
28+
```
29+
30+
### Remove a package
31+
32+
```bash
33+
cd /home/itadmin/code/Nemesis/{path} && uv remove {package}
34+
```
35+
36+
### Sync dependencies
37+
38+
```bash
39+
cd /home/itadmin/code/Nemesis/{path} && uv sync
40+
```
41+
42+
## Project paths
43+
44+
```
45+
projects/web_api/
46+
projects/file_enrichment/
47+
projects/document_conversion/
48+
projects/cli/
49+
projects/alerting/
50+
projects/housekeeping/
51+
projects/agents/
52+
libs/common/
53+
libs/file_enrichment_modules/
54+
libs/chromium/
55+
libs/file_linking/
56+
libs/nemesis_dpapi/
57+
```
58+
59+
## Notes
60+
61+
- Always use `uv`, never `pip`
62+
- Commit both `pyproject.toml` and `uv.lock`
63+
- Use `uv run` to execute commands in the project's environment

CLAUDE.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,21 @@ Nemesis is an open-source, centralized data processing platform (v2.0) that inge
4848
cd projects/web_api && uv sync
4949
```
5050

51-
### Linting & Formatting (Ruff)
51+
### Package Management
52+
53+
For adding, upgrading, or removing packages, use the `/managing-packages` skill.
5254

55+
### Linting & Formatting (Ruff)
56+
Run linting/formatting after each fix/change.
5357
```bash
5458
# Check all Python code (configured in root pyproject.toml)
55-
ruff check .
59+
uv run ruff check . --fix
5660

5761
# Format code
58-
ruff format .
62+
uv run ruff format .
5963

6064
# Check specific project
61-
cd projects/web_api && uv run ruff check .
65+
cd projects/web_api && uv run ruff check . --fix
6266
```
6367

6468
### Testing
@@ -75,22 +79,27 @@ cd projects/web_api && uv run pytest tests/test_file.py::test_function_name
7579
```
7680

7781
### Docker Commands
82+
Information about building dev/prod container images can be found in the [docker compose docs](./docs/docker_compose.md).
7883

84+
General instructions for dev docker containers/images:
7985
```bash
8086
# Build base images first (required before building services)
8187
docker compose -f compose.base.yaml build
8288

8389
# View logs for a service
8490
docker compose logs -f web-api
8591

86-
# Rebuild and restart single service
87-
docker compose up -d --build web-api
92+
# Rebuild Dapr-enabled services (must include the -dapr sidecar)
93+
docker compose up -d --build file-enrichment file-enrichment-dapr
94+
docker compose up -d --build web-api web-api-dapr
8895
```
8996

97+
**Note:** Services using Dapr (file-enrichment, web-api, alerting, etc.) have a companion `-dapr` sidecar container. When rebuilding these services, always restart both the service and its Dapr sidecar to ensure proper communication.
98+
9099
## Architecture
91100

92101
### Tech Stack
93-
- **Python 3.12-3.13** with uv for dependency management
102+
- **Python 3.13** with uv for dependency management
94103
- **FastAPI** for REST services
95104
- **React 18 + Vite + TypeScript** for frontend
96105
- **PostgreSQL** for database

compose.yaml

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,9 @@ services:
238238
interval: 5s
239239
timeout: 5s
240240
retries: 5
241-
volumes: []
242-
# # Uncomment the following line to use custom YARA rules
243-
# - ./libs/file_enrichment_modules/yara_rules/prod/:/yara_rules/:ro
241+
volumes:
242+
# Uncomment the following line to use custom YARA rules
243+
- ./libs/file_enrichment_modules/yara_rules/prod/:/yara_rules/:ro
244244
environment: &file-enrichment-environment
245245
ENABLE_PII_DETECTION: ${ENABLE_PII_DETECTION:-false}
246246
PII_DETECTION_THRESHOLD: ${PII_DETECTION_THRESHOLD:-0.7}
@@ -1083,6 +1083,7 @@ services:
10831083
placement: { condition: service_started }
10841084
rabbitmq: { condition: service_healthy }
10851085
hasura: { condition: service_healthy }
1086+
litellm: { condition: service_healthy }
10861087
healthcheck: *healthcheck-python-svc
10871088
labels:
10881089
- "traefik.enable=true"
@@ -1163,20 +1164,13 @@ services:
11631164
healthcheck:
11641165
test:
11651166
[
1166-
"CMD",
1167-
"wget",
1168-
"--quiet",
1169-
"--tries=1",
1170-
"-O",
1171-
"/dev/null",
1172-
"--header=Authorization: Bearer sk-${LITELLM_ADMIN_PASSWORD:-admin123}",
1173-
"http://localhost:4000/health",
1167+
"CMD-SHELL",
1168+
"python3 -c \"import urllib.request,json; r=urllib.request.urlopen(urllib.request.Request('http://localhost:4000/health',headers={'Authorization':'Bearer sk-${LITELLM_ADMIN_PASSWORD:-admin123}'})); d=json.load(r); exit(0 if d.get('healthy_count',0)>0 and d.get('unhealthy_count',0)==0 else 1)\"",
11741169
]
11751170
interval: 30s
11761171
timeout: 10s
11771172
retries: 3
1178-
start_period: 30s
1179-
disable: true
1173+
start_period: 60s
11801174
logging: *logging-config
11811175
labels:
11821176
- "traefik.enable=true"

docs/agents.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ The Chatbot agent powers the "Chatbot" icon in the left navigation panel. This a
9292

9393
## The Nemesis Web Interface
9494

95-
### Monitoring
95+
### Phoenix LLM Tracing
9696

97-
If the `--monitoring` flag is also passed to the `./tools/nemesis-ctl.sh` script, the [Arize Phoenix](https://github.com/Arize-ai/phoenix) will be deployed to allow tracking of the inputs/outputs sent to the LLM (at the /phoenix route):
97+
When the `--llm` flag is passed to `./tools/nemesis-ctl.sh`, [Arize Phoenix](https://github.com/Arize-ai/phoenix) is deployed (available at the /phoenix route) to allow tracking of the inputs/outputs sent to the LLM.
9898

9999
![Arize Phoenix Tracing](images/arize_phoenix_tracing.png)
100100

101-
If the monitoring profile is used and Arize Phoenix is deployed, the Nemesis frontend will dynamically display links to the Phoenix interface in the Help menu as well.
101+
When Arize Phoenix is deployed, the Nemesis frontend will dynamically display links to the Phoenix interface in the Help menu.
102102

103103
![Nemesis Dynamic Help Menu](images/nemesis_dynamic_help_menu.png)
104104

infra/litellm/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ model_list:
66
aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID
77
aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY
88
aws_region_name: "us-east-1"
9+
910
# # used to triage findings and determine their validity
1011
# - model_name: triage
1112
# litellm_params:

libs/chromium/chromium/chromekey.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ async def retry_decrypt_chrome_key(chrome_key_id: int, dpapi_manager: DpapiManag
5353
logger.warning("Chrome key not found", chrome_key_id=chrome_key_id)
5454
return result
5555

56-
record_id = row["id"]
56+
_record_id = row["id"] # noqa: F841
5757
source = row["source"]
58-
key_masterkey_guid = row["key_masterkey_guid"]
58+
_key_masterkey_guid = row["key_masterkey_guid"] # noqa: F841
5959
key_bytes_enc = row["key_bytes_enc"]
6060
key_bytes_dec = row["key_bytes_dec"]
6161
key_is_decrypted = row["key_is_decrypted"]

libs/chromium/chromium/cookies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ async def _insert_cookies(
140140
if value_dec_bytes:
141141
value_dec = value_dec_bytes.decode("utf-8", errors="replace")
142142
is_decrypted = True
143-
except:
143+
except Exception:
144144
pass
145145

146146
# Get state key ID for key/abe encryption

libs/chromium/chromium/local_state.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,16 +489,16 @@ async def retry_decrypt_state_key(state_key_id: int, dpapi_manager: DpapiManager
489489
logger.warning("State key not found", state_key_id=state_key_id)
490490
return result
491491

492-
record_id = row["id"]
492+
_record_id = row["id"] # noqa: F841
493493
source = row["source"]
494494
username = row["username"]
495495
browser = row["browser"]
496-
key_masterkey_guid = row["key_masterkey_guid"]
496+
_key_masterkey_guid = row["key_masterkey_guid"] # noqa: F841
497497
key_bytes_enc = row["key_bytes_enc"]
498498
key_bytes_dec = row["key_bytes_dec"]
499499
key_is_decrypted = row["key_is_decrypted"]
500500
app_bound_key_enc = row["app_bound_key_enc"]
501-
app_bound_key_system_masterkey_guid = row["app_bound_key_system_masterkey_guid"]
501+
_app_bound_key_system_masterkey_guid = row["app_bound_key_system_masterkey_guid"] # noqa: F841
502502
app_bound_key_user_masterkey_guid = row["app_bound_key_user_masterkey_guid"]
503503
app_bound_key_system_dec = row["app_bound_key_system_dec"]
504504
app_bound_key_user_dec = row["app_bound_key_user_dec"]
@@ -880,7 +880,7 @@ async def retry_decrypt_state_keys_for_chromekey(source: str, chromekey: bytes,
880880
username = row["username"]
881881
browser = row["browser"]
882882
app_bound_key_user_dec = row["app_bound_key_user_dec"]
883-
user_masterkey_guid = row["app_bound_key_user_masterkey_guid"]
883+
_user_masterkey_guid = row["app_bound_key_user_masterkey_guid"] # noqa: F841
884884

885885
result["state_keys_attempted"] += 1
886886

libs/chromium/chromium/logins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ async def _insert_logins(
124124
if password_dec_bytes:
125125
password_value_dec = password_dec_bytes.decode("utf-8", errors="replace")
126126
is_decrypted = True
127-
except:
127+
except Exception:
128128
pass
129129

130130
# Get state key ID for key/abe encryption

libs/chromium/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
description = "Modules Nemesis uses to handle Chromium files"
55
authors = [{name = "SpecterOps"}]
66
readme = "README.md"
7-
requires-python = ">=3.12,<3.14"
7+
requires-python = ">=3.13,<3.14"
88

99
dependencies = [
1010
"impacket>=0.13.0,<0.14.0",

0 commit comments

Comments
 (0)