netaudit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. netaudit-0.1.0/.claude/commands/commit.md +22 -0
  2. netaudit-0.1.0/.github/pull_request_template.md +11 -0
  3. netaudit-0.1.0/.github/workflows/ci.yml +60 -0
  4. netaudit-0.1.0/.gitignore +30 -0
  5. netaudit-0.1.0/.instructions/plan.md +271 -0
  6. netaudit-0.1.0/.readthedocs.yaml +16 -0
  7. netaudit-0.1.0/CLAUDE.md +111 -0
  8. netaudit-0.1.0/LICENSE +201 -0
  9. netaudit-0.1.0/Makefile +28 -0
  10. netaudit-0.1.0/PKG-INFO +302 -0
  11. netaudit-0.1.0/README.md +69 -0
  12. netaudit-0.1.0/docs/allowlist-dsl.md +138 -0
  13. netaudit-0.1.0/docs/architecture.md +116 -0
  14. netaudit-0.1.0/docs/cli-reference.md +118 -0
  15. netaudit-0.1.0/docs/index.md +64 -0
  16. netaudit-0.1.0/mkdocs.yml +46 -0
  17. netaudit-0.1.0/netaudit/__init__.py +3 -0
  18. netaudit-0.1.0/netaudit/allowlist.py +130 -0
  19. netaudit-0.1.0/netaudit/cli.py +109 -0
  20. netaudit-0.1.0/netaudit/integrations/__init__.py +1 -0
  21. netaudit-0.1.0/netaudit/parser.py +193 -0
  22. netaudit-0.1.0/netaudit/py.typed +0 -0
  23. netaudit-0.1.0/netaudit/reporter.py +110 -0
  24. netaudit-0.1.0/netaudit/runner.py +66 -0
  25. netaudit-0.1.0/pyproject.toml +58 -0
  26. netaudit-0.1.0/tests/conftest.py +0 -0
  27. netaudit-0.1.0/tests/integration/__init__.py +0 -0
  28. netaudit-0.1.0/tests/integration/conftest.py +1 -0
  29. netaudit-0.1.0/tests/integration/egress_target.py +84 -0
  30. netaudit-0.1.0/tests/integration/test_end_to_end.py +104 -0
  31. netaudit-0.1.0/tests/test_allowlist.py +185 -0
  32. netaudit-0.1.0/tests/test_cli.py +211 -0
  33. netaudit-0.1.0/tests/test_parser.py +177 -0
  34. netaudit-0.1.0/tests/test_reporter.py +128 -0
  35. netaudit-0.1.0/tests/test_version.py +6 -0
@@ -0,0 +1,22 @@
1
+ Look at the current git diff (staged + unstaged) and create a GPG-signed conventional commit.
2
+
3
+ Steps:
4
+ 1. Run `git diff HEAD` to see all changes (staged and unstaged). If nothing, run `git status` to check for untracked files.
5
+ 2. Determine the commit type from the diff:
6
+ - `feat`: new user-visible functionality
7
+ - `fix`: bug fix
8
+ - `refactor`: restructuring without behavior change
9
+ - `test`: test additions or changes only
10
+ - `docs`: documentation only
11
+ - `chore`: tooling, config, build, CI, deps
12
+ 3. Stage all relevant changes with `git add` (specific files, not `-A`).
13
+ 4. Write a single-line commit message: `<type>(<scope>): <short description>` — scope is optional, description is lowercase, no trailing period, under 72 chars total.
14
+ 5. Commit with `git commit -S -m "<message>"` (GPG-signed, no co-author line).
15
+
16
+ Rules:
17
+ - No co-author trailer.
18
+ - No body or footer unless the change genuinely needs explanation.
19
+ - Do not skip hooks (`--no-verify`).
20
+ - If GPG signing fails, report the error and stop — do not commit unsigned.
21
+ - Do not push.
22
+ - Never reference phase numbers (e.g. "phase 0", "phase 1") in the commit message — describe what was done, not which plan phase it belongs to.
@@ -0,0 +1,11 @@
1
+ ## Title
2
+ <!-- conventional commit format: type: short subject (e.g. feat: add HTTP/SSE transport) -->
3
+
4
+ ## What
5
+ <!-- one or two sentences describing what was built or changed -->
6
+
7
+ ## Why
8
+ <!-- what problem it solves or what it enables -->
9
+
10
+ ## Acceptance criteria
11
+ - [ ] <!-- mark with x if the criterion is verified -->
@@ -0,0 +1,60 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ branches: [main, develop]
8
+ jobs:
9
+ lint:
10
+ name: Lint
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.11"
17
+ - name: Install dev dependencies
18
+ run: pip install -e ".[dev]"
19
+ - name: ruff check
20
+ run: ruff check .
21
+ - name: ruff format
22
+ run: ruff format --check .
23
+ - name: mypy
24
+ run: mypy netaudit/
25
+ - name: Install docs dependencies
26
+ run: pip install -e ".[docs]"
27
+ - name: mkdocs build
28
+ run: mkdocs build --strict
29
+
30
+ test:
31
+ name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
32
+ runs-on: ${{ matrix.os }}
33
+ strategy:
34
+ matrix:
35
+ os: [ubuntu-latest, macos-latest, windows-latest]
36
+ python-version: ["3.11", "3.12"]
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - uses: actions/setup-python@v5
40
+ with:
41
+ python-version: ${{ matrix.python-version }}
42
+ - name: Install dev dependencies
43
+ run: pip install -e ".[dev]"
44
+ - name: Run tests
45
+ run: pytest --cov=netaudit --cov-fail-under=80
46
+
47
+ integration:
48
+ name: Integration Tests (strace)
49
+ runs-on: ubuntu-latest
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ - uses: actions/setup-python@v5
53
+ with:
54
+ python-version: "3.11"
55
+ - name: Install strace
56
+ run: sudo apt-get install -y strace
57
+ - name: Install dev dependencies
58
+ run: pip install -e ".[dev]"
59
+ - name: Run integration tests
60
+ run: pytest -m integration -v
@@ -0,0 +1,30 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ *.so
7
+ *.egg
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ .eggs/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .pytest_cache/
15
+ .coverage
16
+ htmlcov/
17
+
18
+ # Virtual environment
19
+ .venv/
20
+
21
+ # netaudit
22
+ .strace-out/
23
+
24
+ # Docs
25
+ site/
26
+
27
+ # Distribution
28
+ *.whl
29
+ *.tar.gz
30
+ MANIFEST
@@ -0,0 +1,271 @@
1
+ # Netaudit Development Plan (Refined)
2
+
3
+ ## Context
4
+
5
+ netaudit is a Python CLI/library that wraps processes under `strace`, captures `connect()` syscalls, and validates them against a declarative YAML allowlist. Goal: CI-native network egress auditing — commit a config, run tests, get pass/fail.
6
+
7
+ Starting from scratch on `main` (README + LICENSE only, no remote). Each phase produces a testable, CI-validated increment.
8
+
9
+ ### Decisions
10
+
11
+ - **Versioning:** 0.x semver until API stabilizes, then 1.0
12
+ - **First public release:** Phase 3 (when CLI is usable), tagged 0.1.0
13
+ - **Docs:** MkDocs Material, deployed to GitHub Pages — deferred until Phase 3 (docstrings + README before that)
14
+ - **CI:** Ubuntu-only for integration tests, cross-platform for unit tests
15
+ - **Changelog:** None until v1
16
+ - **pytest plugin:** Session-level first, per-test attribution second commit in same phase
17
+ - **Type checking:** mypy strict from Phase 0, `py.typed` marker included
18
+ - **Skills:** Only `/commit` from day one; others added when needed
19
+
20
+ ---
21
+
22
+ ## Phase Overview
23
+
24
+ ```
25
+ Phase 0: Scaffold ──► Phase 1: Core Engine ──► Phase 2: Runner + Integration
26
+ │ (parser+allowlist+ (strace subprocess,
27
+ │ reporter) end-to-end tests)
28
+ │ │
29
+ ▼ ▼
30
+ CI v1 (lint+test) CI v2 (+integration on Ubuntu)
31
+
32
+
33
+ Phase 3: CLI + Docs
34
+ (click CLI, mkdocs,
35
+ first release 0.1.0)
36
+
37
+
38
+ Phase 4: pytest Plugin
39
+ (session-level, then
40
+ per-test attribution)
41
+
42
+
43
+ Phase 5: Release Engineering
44
+ (PyPI, Docker, GH Pages,
45
+ hardening)
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Phase 0 — Scaffold
51
+
52
+ **Goal:** Buildable, installable, lintable skeleton with CI.
53
+
54
+ - `pyproject.toml`: metadata, deps (`click`, `pyyaml`), dev deps (`pytest`, `ruff`, `mypy`, `pytest-cov`), entry point `netaudit = netaudit.cli:main`
55
+ - Package: `netaudit/__init__.py` (version string), `netaudit/integrations/__init__.py`
56
+ - `py.typed` marker
57
+ - `.gitignore` (Python template + `.strace-out/` + `.venv/`)
58
+ - Ruff config: `line-length=100`, `select = ["E", "F", "W", "I"]`
59
+ - Mypy config: `strict = true`
60
+ - Empty `tests/conftest.py`
61
+ - `.venv` local development — created via `python -m venv .venv`, editable install with dev deps
62
+ - `Makefile` with `venv`, `lint`, `test`, `clean` targets for one-command local workflow
63
+ - GitHub Actions CI (`.github/workflows/ci.yml`): lint (ruff check + ruff format --check + mypy), test (pytest, no tests yet)
64
+ - `.claude/commands/commit.md`: conventional commit skill — auto-detects type from diff, GPG-signed, no co-author
65
+
66
+ **Exit:** `make venv && make lint && make test` works, CI green, `netaudit --help` prints placeholder.
67
+
68
+ **Files:**
69
+ ```
70
+ pyproject.toml
71
+ Makefile
72
+ .gitignore
73
+ netaudit/__init__.py
74
+ netaudit/py.typed
75
+ netaudit/cli.py # placeholder: click group, --version
76
+ netaudit/integrations/__init__.py
77
+ tests/conftest.py
78
+ .github/workflows/ci.yml
79
+ .claude/commands/commit.md
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Phase 1 — Core Engine (Parser + Allowlist + Reporter)
85
+
86
+ **Goal:** Parse strace output, match against rules, report violations. All tested without strace.
87
+
88
+ **Parser** (`netaudit/parser.py`):
89
+ - `ConnectEvent` dataclass: `pid`, `timestamp`, `family`, `addr`, `port`, `raw_line`, `result`
90
+ - `StraceParser.parse_line(line) -> ConnectEvent | None`
91
+ - `StraceParser.parse_stream(lines: Iterable[str]) -> list[ConnectEvent]`
92
+ - Handle: AF_INET, AF_INET6, AF_UNIX, AF_NETLINK
93
+ - Handle: EINPROGRESS (non-blocking connect — not a failure), resumed lines (`<... connect resumed>`)
94
+
95
+ **Allowlist** (`netaudit/allowlist.py`):
96
+ - `Rule` protocol: `matches(event: ConnectEvent) -> bool`
97
+ - Concrete: `IPv4Rule(cidr)`, `IPv6Rule(cidr)`, `UnixSocketRule(path_glob)`, `NetlinkRule`
98
+ - `AllowList`: load from YAML, `includes_builtins: true` by default (loopback 127.0.0.0/8, ::1/128, all AF_UNIX, all AF_NETLINK)
99
+ - `AllowList.is_allowed(event) -> bool`
100
+
101
+ **Reporter** (`netaudit/reporter.py`):
102
+ - `Violation`: groups events by `(family, addr, port)` — tracks PIDs, count, first timestamp
103
+ - `Reporter.check(events, allowlist) -> list[Violation]`
104
+ - `Reporter.format(violations, stream)` — box-formatted human-readable output
105
+
106
+ **Tests:** hardcoded strace lines for parser (all families, EINPROGRESS, resumed, malformed), YAML loading + CIDR matching + builtins for allowlist, grouping + formatting for reporter.
107
+
108
+ **CI update:** Tests run with `--cov=netaudit --cov-fail-under=80`.
109
+
110
+ **Exit:** All three modules compose together. Tests pass, coverage >= 80%.
111
+
112
+ ---
113
+
114
+ ## Phase 2 — Runner + Integration Tests
115
+
116
+ **Goal:** Spawn strace, capture output, validate end-to-end on real syscalls.
117
+
118
+ **Runner** (`netaudit/runner.py`):
119
+ - `StraceRunner.run(command, output_path) -> CompletedProcess` — spawn under `strace -e trace=connect -f -tt -o <path>`
120
+ - `StraceRunner.start(command, output_path) -> StraceProcess` / `.stop() -> CompletedProcess`
121
+ - Runtime check: `StraceNotFoundError` if `shutil.which("strace")` is None
122
+
123
+ **Test target** (`tests/integration/egress_target.py`):
124
+ - Starts a local `http.server`, connects to it
125
+ - Opens a Unix socket
126
+ - Triggers DNS/netlink via `socket.getaddrinfo`
127
+ - Attempts external connect to `198.51.100.1:443` (TEST-NET-2 — won't route, no flaky external deps)
128
+
129
+ **Integration tests** (`tests/integration/test_end_to_end.py`, marked `@pytest.mark.integration`):
130
+ - strace output file created and non-empty
131
+ - Parser extracts expected event types from real output
132
+ - External IP `198.51.100.1` produces violation
133
+ - Loopback + unix + netlink allowed by default
134
+ - Custom allowlist passes external IP
135
+
136
+ **CI update:** Add `integration` job on `ubuntu-latest` (installs strace via apt). Unit tests remain cross-platform.
137
+
138
+ **Exit:** Full pipeline works end-to-end. Integration tests green in CI.
139
+
140
+ ---
141
+
142
+ ## Phase 3 — CLI + Docs (First Release 0.1.0)
143
+
144
+ **Goal:** User-facing CLI, documentation site, tagged 0.1.0.
145
+
146
+ **CLI** (`netaudit/cli.py`):
147
+ - `netaudit run --allowlist <yaml> -- <command>` — trace, analyze, report, exit 1 on violations
148
+ - `netaudit analyze --allowlist <yaml> <strace-log>` — offline analysis
149
+ - `--format {text,json}` for machine-readable output
150
+ - `--allowlist` defaults to `netaudit.yaml` in cwd if it exists
151
+ - Exit codes: 0 clean, 1 violations, 2 strace missing
152
+
153
+ **Tests** (`tests/test_cli.py`):
154
+ - Click `CliRunner` tests (mocked runner for `run`, real file for `analyze`)
155
+ - Exit code semantics
156
+ - Integration test: real `netaudit run` invocation
157
+
158
+ **Docs** (`docs/` + `mkdocs.yml`):
159
+ - MkDocs Material: nav, search, code highlighting
160
+ - Pages: index (overview + quickstart), cli-reference, allowlist-dsl, architecture
161
+ - README.md updated with badges, install instructions, quickstart
162
+
163
+ **CI update:** Add `mkdocs build --strict` to lint job.
164
+
165
+ **Exit:** Both CLI commands work. Docs build. Tagged `v0.1.0`.
166
+
167
+ ---
168
+
169
+ ## Phase 4 — pytest Plugin
170
+
171
+ **Goal:** Automatic network auditing during pytest runs.
172
+
173
+ **Session-level** (first commit):
174
+ - `netaudit/integrations/pytest_plugin.py`, registered via `pytest11` entry point
175
+ - `pytest_addoption`: `--netaudit`, `--netaudit-allowlist`
176
+ - `pytest_sessionstart`: start strace tracing the test process
177
+ - `pytest_sessionfinish`: stop, parse, check, report violations, set exit code
178
+ - Allowlist resolution: CLI flag > `[tool.netaudit]` in pyproject.toml > `netaudit.yaml` in cwd
179
+
180
+ **Per-test attribution** (second commit):
181
+ - `pytest_runtest_protocol`: write timestamp markers to sidecar file at test boundaries
182
+ - Correlate ConnectEvents with test items by timestamp range
183
+ - Reporter enriched: violations grouped by test name
184
+
185
+ **Tests:** `pytester`-based tests for both modes.
186
+
187
+ **Docs:** `docs/pytest-plugin.md` — usage, config, examples.
188
+
189
+ **Exit:** `pytest --netaudit` traces and reports. Per-test attribution shows which test caused each violation.
190
+
191
+ ---
192
+
193
+ ## Phase 5 — Release Engineering
194
+
195
+ **Goal:** Automated publishing, Docker image, hardening.
196
+
197
+ - **PyPI workflow** (`.github/workflows/release.yml`): build + publish on GitHub release
198
+ - **Docs deploy** (`.github/workflows/docs.yml`): `mkdocs gh-deploy` on push to main
199
+ - **Dockerfile**: `python:3.12-slim` + strace, `ENTRYPOINT ["netaudit"]`
200
+ - **Docker CI**: build + push to GHCR on release
201
+ - **Hardening**: truncated strace output, binary garbage in paths, long lines, permission errors, signal handling
202
+ - **Docs:** Docker usage page, contributing guide
203
+
204
+ **Exit:** `pip install netaudit` from PyPI, `docker run` works, docs live on GH Pages.
205
+
206
+ ---
207
+
208
+ ## Skills
209
+
210
+ | Skill | Created in | Description |
211
+ |---|---|---|
212
+ | `/commit` | Phase 0 | Auto-detect conventional commit type from diff (feat/fix/refactor/test/docs/chore), minimal description, GPG-signed, no co-author |
213
+
214
+ Additional skills (`/phase-docs`, `/coverage-check`, `/release-prep`) to be created when the need arises, not upfront.
215
+
216
+ ---
217
+
218
+ ## Verification
219
+
220
+ Each phase verified by:
221
+ 1. `ruff check . && ruff format --check . && mypy netaudit/` — lint clean
222
+ 2. `pytest --cov=netaudit --cov-fail-under=80` — tests pass with coverage
223
+ 3. `pytest -m integration` (Linux only) — real strace tests (from Phase 2)
224
+ 4. `mkdocs build --strict` — docs build (from Phase 3)
225
+ 5. CI green on push
226
+
227
+ ---
228
+
229
+ ## Key Files (final state)
230
+
231
+ ```
232
+ pyproject.toml
233
+ Makefile
234
+ .gitignore
235
+ mkdocs.yml
236
+ Dockerfile
237
+ netaudit/
238
+ __init__.py
239
+ py.typed
240
+ parser.py
241
+ allowlist.py
242
+ reporter.py
243
+ runner.py
244
+ cli.py
245
+ integrations/
246
+ __init__.py
247
+ pytest_plugin.py
248
+ tests/
249
+ conftest.py
250
+ test_parser.py
251
+ test_allowlist.py
252
+ test_reporter.py
253
+ test_cli.py
254
+ integration/
255
+ conftest.py
256
+ egress_target.py
257
+ test_end_to_end.py
258
+ docs/
259
+ index.md
260
+ architecture.md
261
+ cli-reference.md
262
+ allowlist-dsl.md
263
+ pytest-plugin.md
264
+ docker.md
265
+ .github/workflows/
266
+ ci.yml
267
+ docs.yml
268
+ release.yml
269
+ .claude/commands/
270
+ commit.md
271
+ ```
@@ -0,0 +1,16 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3.11"
7
+
8
+ mkdocs:
9
+ configuration: mkdocs.yml
10
+
11
+ python:
12
+ install:
13
+ - method: pip
14
+ path: .
15
+ extra_requirements:
16
+ - docs
@@ -0,0 +1,111 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this project is
6
+
7
+ `netaudit` is a Python library and CLI that wraps a process or test suite under `strace`, collects all `connect()` syscalls, filters them against a declarative allowlist, and reports violations. The goal: commit a config file declaring what's allowed, run tests normally, get pass/fail with readable output instead of raw strace noise.
8
+
9
+ `strace` is a **system dependency** — documented, not bundled. No other runtime dependencies. `pytest` is optional (only needed for the plugin).
10
+
11
+ ## Commands
12
+
13
+ ```bash
14
+ # Set up venv (Python 3.11+)
15
+ python3.11 -m venv .venv
16
+ .venv/bin/pip install -e ".[dev]"
17
+
18
+ # Run all tests
19
+ .venv/bin/pytest
20
+
21
+ # Run a single test
22
+ .venv/bin/pytest tests/test_parser.py::TestStraceParser::test_parse_af_inet_connect
23
+
24
+ # CLI usage
25
+ .venv/bin/netaudit run --allowlist netaudit.yaml -- <command>
26
+ .venv/bin/netaudit analyze --allowlist netaudit.yaml /tmp/strace.log
27
+ ```
28
+
29
+ ## Architecture
30
+
31
+ Two-layer design: a **language-agnostic core** (syscall tracing + analysis) and **framework-specific integrations** that add execution context (e.g. test attribution). The core must not import anything from integrations.
32
+
33
+ ### Core (`netaudit/`)
34
+
35
+ | Module | Role |
36
+ |---|---|
37
+ | `runner.py` | `StraceRunner` — spawns strace as a subprocess wrapping a command, or attaches to an existing PID via `attach(pid)` |
38
+ | `parser.py` | `StraceParser` — line-by-line regex parser producing `ConnectEvent` dataclasses |
39
+ | `allowlist.py` | `AllowList` + rule types: `IPv4`, `IPv6`, `UnixSocket`, `Netlink` — loaded from YAML or constructed programmatically |
40
+ | `reporter.py` | `Violation`, `Reporter` — groups events, formats output; enriched with test attribution when markers are available |
41
+ | `cli.py` | CLI entry point (`netaudit run` / `netaudit analyze`) |
42
+
43
+ ### `ConnectEvent` (central data type)
44
+
45
+ ```python
46
+ @dataclass
47
+ class ConnectEvent:
48
+ pid: int
49
+ timestamp: float
50
+ family: str # "AF_INET", "AF_INET6", "AF_UNIX", "AF_NETLINK", ...
51
+ addr: str | None # IP or socket path
52
+ port: int | None
53
+ result: int # 0 = success; negative errno (e.g. -ECONNREFUSED)
54
+ ```
55
+
56
+ ### Parser edge cases to handle
57
+
58
+ - `EINPROGRESS`: non-blocking connect in flight — not a failure, should still be evaluated against the allowlist
59
+ - Multi-line strace continuations (syscall split across lines)
60
+ - Thread interleavings when strace is run with `-f`
61
+
62
+ ### AllowList DSL
63
+
64
+ YAML format committed to the repo:
65
+
66
+ ```yaml
67
+ version: 1
68
+ allowlist:
69
+ - comment: "GVM Unix socket"
70
+ family: AF_UNIX
71
+ path_prefix: /run/gvmd/
72
+ - comment: "GVM TCP proxy"
73
+ family: AF_INET
74
+ addr: 127.0.0.1
75
+ port: 9393
76
+ - comment: "IPv6 loopback"
77
+ family: AF_INET6
78
+ addr: "::1"
79
+ - comment: "glibc resolver internals"
80
+ family: AF_NETLINK
81
+ ```
82
+
83
+ `Netlink()` and loopback are reasonable built-in defaults — users opt **out** rather than explicitly allowing them.
84
+
85
+ ### pytest integration (`netaudit/integrations/pytest_plugin.py`)
86
+
87
+ Registered via `entry_points` in `pyproject.toml` (not imported directly). Re-execs the pytest process with strace as parent. Emits timestamp markers at session and test boundaries so violations can be attributed to individual test cases. Calls `pytest.fail()` after `pytest_sessionfinish` if violations exist.
88
+
89
+ Activated via `pyproject.toml`:
90
+ ```toml
91
+ [tool.netaudit]
92
+ allowlist = "netaudit.yaml"
93
+ enabled = true
94
+ ```
95
+
96
+ ### Future integrations
97
+
98
+ Node.js (Jest/Vitest/Mocha) and other runners are explicitly in scope. All integrations follow the same pattern: **emit execution markers → correlate with syscall timestamps → enrich violation reports**. New integrations go in `netaudit/integrations/` and must not require changes to core modules.
99
+
100
+ ## Key design constraints
101
+
102
+ - The core is entirely framework-agnostic — no pytest imports outside `integrations/`
103
+ - Exit code is non-zero on any violation (CI-native)
104
+ - The CLI (`netaudit run`) must work with any executable, not just Python test suites
105
+ - Regex is sufficient for parsing strace output — the format is stable and well-documented
106
+ - `mypy --strict` enforced from day one; `py.typed` marker included
107
+ - `ruff` for linting and formatting (line-length=100, select E,F,W,I)
108
+
109
+ ## Development plan
110
+
111
+ See [`.instructions/plan.md`](.instructions/plan.md) for the full phased development plan (6 phases, agile approach with CI/CD from Phase 0).