Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ __pycache__/
dist/
build/
.venv/
.vscode/
.vscode/
.mypy_cache/
.pytest_cache/
.ruff_cache/
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.1] - 2026-03-28

### Fixed

- Use module logger (`LOG.debug`) instead of root `logging.debug` in `_detect_hubble_cmd`.
- Replace `assert` guards in reader threads with early-return checks (assertions are stripped under `python -O`).
- Guard `cursor_flow_idx` when `ordered_keys` is empty in watch select mode.
- Close capture file handle on early exit paths to prevent resource leak.
- Add missing `py.typed` marker file (referenced in CHANGELOG since v0.4.0 but never created).
- Add `types-PyYAML` to dev dependencies so local `mypy` works without manual installs.
- Add `.mypy_cache/`, `.pytest_cache/`, `.ruff_cache/` to `.gitignore`.

## [0.7.0] - 2026-03-27

### Added
Expand Down
20 changes: 14 additions & 6 deletions hubble_audit2policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from __future__ import annotations

__version__ = "0.7.0"
__version__ = "0.7.1"
__author__ = "noexecstack"
__license__ = "Apache-2.0"

Expand Down Expand Up @@ -1013,15 +1013,15 @@ def _detect_hubble_cmd() -> tuple[list[str], bool]:
only when appropriate.
"""
if shutil.which("hubble"):
logging.debug("hubble: using PATH binary with -P (auto port-forward)")
LOG.debug("hubble: using PATH binary with -P (auto port-forward)")
return ["hubble", "observe"], True
# Fall back to kubectl exec into a Cilium pod.
if shutil.which("kubectl"):
try:
cilium_node_map = _build_cilium_node_map()
if cilium_node_map:
cilium_pod = next(iter(sorted(cilium_node_map.values())))
logging.debug("hubble: using kubectl exec into %s", cilium_pod)
LOG.debug("hubble: using kubectl exec into %s", cilium_pod)
return [
"kubectl",
"exec",
Expand Down Expand Up @@ -1075,7 +1075,8 @@ def _hubble_reader_thread(
stop_event: threading.Event,
) -> None:
"""Read JSON flow lines from hubble stdout and feed them into *store*."""
assert proc.stdout is not None
if proc.stdout is None:
return
for line in proc.stdout:
if stop_event.is_set():
break
Expand All @@ -1100,7 +1101,8 @@ def _hubble_stderr_thread(
stop_event: threading.Event,
) -> None:
"""Drain hubble stderr and record the last meaningful line in *store*."""
assert proc.stderr is not None
if proc.stderr is None:
return
for line in proc.stderr:
if stop_event.is_set():
break
Expand Down Expand Up @@ -1218,6 +1220,8 @@ def _watch_mode(args: argparse.Namespace) -> None:
try:
hubble_cmd = _build_hubble_observe_cmd(args)
except RuntimeError as exc:
if capture_fh:
capture_fh.close()
LOG.error("%s", exc)
sys.exit(EXIT_ERROR)

Expand All @@ -1230,6 +1234,8 @@ def _watch_mode(args: argparse.Namespace) -> None:
try:
proc_holder = [_launch_hubble(hubble_cmd, store, stop_event)]
except FileNotFoundError:
if capture_fh:
capture_fh.close()
LOG.error("Command not found: %s", hubble_cmd[0])
sys.exit(EXIT_ERROR)

Expand Down Expand Up @@ -1297,9 +1303,11 @@ def _run(stdscr: curses.window) -> None: # type: ignore[name-defined]
is_paused = not is_paused

elif key in (ord("s"), ord("S")): # toggle select mode
if not is_selecting and not ordered_keys:
continue # nothing to select
is_selecting = not is_selecting
if is_selecting:
cursor_flow_idx = min(cursor_flow_idx, max(0, len(ordered_keys) - 1))
cursor_flow_idx = min(cursor_flow_idx, len(ordered_keys) - 1)

elif key == 27: # Esc – exit select + clear
is_selecting = False
Expand Down
Empty file added py.typed
Empty file.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "hubble-audit2policy"
version = "0.7.0"
version = "0.7.1"
description = "Generate least-privilege CiliumNetworkPolicy YAML from Hubble flow logs."
readme = "README.md"
license = "Apache-2.0"
Expand All @@ -26,7 +26,7 @@ classifiers = [
dependencies = ["pyyaml>=6.0"]

[project.optional-dependencies]
dev = ["pytest>=7.0", "ruff>=0.4", "mypy>=1.10"]
dev = ["pytest>=7.0", "ruff>=0.4", "mypy>=1.10", "types-PyYAML>=6.0"]

[project.scripts]
hubble-audit2policy = "hubble_audit2policy:main"
Expand Down
Loading