mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-28 21:17:36 +00:00
docs: Anti-Track v2 Plan 2b IP-drop implementation plan (ref #633)
This commit is contained in:
parent
ee3a9aee4d
commit
78a049c4b2
|
|
@ -0,0 +1,485 @@
|
|||
# Anti-Track v2 — Plan 2b (Exclusive-IP nft-drop) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Hard-block at the IP layer — resolve confirmed **pure-tracker** domains to IPs and drop them into the existing nft `secubox_blacklist` set, but only for IPs not in a curated CDN/cloud allowlist, gated dark behind `privacy_enforce && privacy_ip_drop`.
|
||||
|
||||
**Architecture:** A new pure module `secubox_toolbox/ip_dns.py` holds the CDN-allowlist parsing and the exclusivity decision (no nft/DNS/network — takes a `resolve` callable + pre-loaded networks). `escalate.py` (which already owns `_resolve_ips`, `_nft_add_blacklist`, audit, and runs on the `secubox-escalate` timer) gains one gated step that reads `pure-trackers.txt`, applies the decision, and drops the IPs. A static `data/cdn-allowlist.txt` ships in the .deb.
|
||||
|
||||
**Tech Stack:** Python 3.11 stdlib `ipaddress`, the existing `escalate.py` nft/resolve plumbing, `secubox_toolbox.filters` (Plan 1 toggles). pytest. Issue #633.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-06-17-anti-track-v2-plan2b-enforcement-design.md` (§3, §4). **The §5 DNS-refuse feed is split to a separate follow-up plan** pending resolver-topology confirmation — NOT in this plan.
|
||||
|
||||
**Scope note:** Plan 2b-of-this-plan = IP layer only. DNS feed deferred. Conventions: SPDX header on new `.py`; paths relative to `packages/secubox-toolbox/`; worktree `secubox-deb-worktrees/633-…` branch `feature/633-…`; commits end `(ref #633)`.
|
||||
|
||||
**Verified facts:** `escalate.py` — `TABLE="inet secubox_blacklist"`, `_resolve_ips(host, timeout=2.0)->List[str]`, `_nft_add_blacklist(ip)->bool` (adds `{ ip timeout ESCALATE_TTL }` to `blacklist_v4`/`v6`), `_audit(msg)`, `ESCALATE_TTL` env default `"4h"`, `evaluate_and_apply()` is the timer entry. Plan 1 `filters.py` keys: `privacy_enforce` (default False), `privacy_ip_drop` (default False). Plan 2a writes `/var/lib/secubox/toolbox/pure-trackers.txt`.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: `ip_dns.py` — CDN allowlist parsing + membership
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/secubox-toolbox/secubox_toolbox/ip_dns.py`
|
||||
- Test: `packages/secubox-toolbox/tests/test_ip_dns.py`
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
```python
|
||||
# packages/secubox-toolbox/tests/test_ip_dns.py
|
||||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
from secubox_toolbox import ip_dns
|
||||
|
||||
|
||||
def test_load_cdn_allowlist_parses_and_skips(tmp_path):
|
||||
f = tmp_path / "cdn.txt"
|
||||
f.write_text(
|
||||
"# comment\n"
|
||||
"104.16.0.0/13\n" # Cloudflare v4
|
||||
"\n"
|
||||
"2606:4700::/32\n" # Cloudflare v6
|
||||
"not-a-cidr\n" # malformed → skipped
|
||||
)
|
||||
nets = ip_dns.load_cdn_allowlist(str(f))
|
||||
assert len(nets) == 2
|
||||
|
||||
|
||||
def test_ip_in_allowlist_v4_and_v6(tmp_path):
|
||||
f = tmp_path / "cdn.txt"
|
||||
f.write_text("104.16.0.0/13\n2606:4700::/32\n")
|
||||
nets = ip_dns.load_cdn_allowlist(str(f))
|
||||
assert ip_dns.ip_in_allowlist("104.16.5.5", nets) is True
|
||||
assert ip_dns.ip_in_allowlist("8.8.8.8", nets) is False
|
||||
assert ip_dns.ip_in_allowlist("2606:4700::1111", nets) is True
|
||||
assert ip_dns.ip_in_allowlist("garbage", nets) is False
|
||||
|
||||
|
||||
def test_load_missing_file_returns_empty():
|
||||
assert ip_dns.load_cdn_allowlist("/no/such/file.txt") == []
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/test_ip_dns.py -q`
|
||||
Expected: FAIL — `ModuleNotFoundError: No module named 'secubox_toolbox.ip_dns'`
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
```python
|
||||
# packages/secubox-toolbox/secubox_toolbox/ip_dns.py
|
||||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
# Source-Disclosed License — All rights reserved except as expressly granted.
|
||||
# See LICENCE-CMSD-1.0.md for terms.
|
||||
|
||||
"""
|
||||
SecuBox-Deb :: toolbox :: Anti-Track v2 Plan 2b IP/DNS helpers (#633)
|
||||
|
||||
Pure functions — no nft, no DNS, no file writes beyond reading the static CDN
|
||||
allowlist. Consumed by escalate.py (exclusive-IP nft-drop). The CDN/cloud
|
||||
allowlist is the collateral gate: an IP that belongs to shared infrastructure
|
||||
(Cloudflare/Fastly/Akamai/Google/AWS/Azure) is NEVER dropped, even when a pure
|
||||
tracker resolves there.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Callable, Iterable, List
|
||||
|
||||
log = logging.getLogger("secubox.toolbox.ip_dns")
|
||||
|
||||
CDN_ALLOWLIST_PATH = "/usr/lib/secubox/toolbox/data/cdn-allowlist.txt"
|
||||
|
||||
|
||||
def load_cdn_allowlist(path: str = CDN_ALLOWLIST_PATH) -> list:
|
||||
"""Parse a CIDR-per-line file into ip_network objects. Comments (#) and
|
||||
blank/malformed lines are skipped. Missing file → []."""
|
||||
nets = []
|
||||
try:
|
||||
text = Path(path).read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return nets
|
||||
for line in text.splitlines():
|
||||
s = line.strip()
|
||||
if not s or s.startswith("#"):
|
||||
continue
|
||||
try:
|
||||
nets.append(ipaddress.ip_network(s, strict=False))
|
||||
except ValueError:
|
||||
log.warning("cdn-allowlist: skipping malformed CIDR %r", s)
|
||||
return nets
|
||||
|
||||
|
||||
def ip_in_allowlist(ip: str, networks: Iterable) -> bool:
|
||||
"""True if ip falls inside any allow-network. Malformed ip → False."""
|
||||
try:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
except ValueError:
|
||||
return False
|
||||
for net in networks:
|
||||
if addr.version == net.version and addr in net:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/test_ip_dns.py -q`
|
||||
Expected: PASS (3 passed)
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/secubox-toolbox/secubox_toolbox/ip_dns.py packages/secubox-toolbox/tests/test_ip_dns.py
|
||||
git commit -m "feat(toolbox): ip_dns CDN allowlist parsing + membership (ref #633)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: `ip_dns.exclusive_tracker_ips`
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/secubox-toolbox/secubox_toolbox/ip_dns.py`
|
||||
- Test: `packages/secubox-toolbox/tests/test_ip_dns.py`
|
||||
|
||||
- [ ] **Step 1: Write the failing test (append)**
|
||||
|
||||
```python
|
||||
def test_exclusive_tracker_ips_excludes_cdn(tmp_path):
|
||||
f = tmp_path / "cdn.txt"
|
||||
f.write_text("104.16.0.0/13\n") # Cloudflare range
|
||||
nets = ip_dns.load_cdn_allowlist(str(f))
|
||||
# stub resolver: pure1 → a non-CDN IP ; pure2 → a Cloudflare IP
|
||||
resolve = {"pure1.trk": ["203.0.113.7"],
|
||||
"pure2.trk": ["104.16.9.9"]}.get
|
||||
out = ip_dns.exclusive_tracker_ips(
|
||||
["pure1.trk", "pure2.trk"], lambda h: resolve(h) or [], nets)
|
||||
assert out == {"203.0.113.7"} # CDN IP excluded
|
||||
|
||||
|
||||
def test_exclusive_tracker_ips_dedups_and_handles_empty():
|
||||
out = ip_dns.exclusive_tracker_ips(
|
||||
["a.trk", "b.trk"],
|
||||
lambda h: ["198.51.100.5"], # both resolve to same IP
|
||||
[])
|
||||
assert out == {"198.51.100.5"}
|
||||
assert ip_dns.exclusive_tracker_ips([], lambda h: ["1.2.3.4"], []) == set()
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/test_ip_dns.py -q`
|
||||
Expected: FAIL — `AttributeError: module 'secubox_toolbox.ip_dns' has no attribute 'exclusive_tracker_ips'`
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation (append to `ip_dns.py`)**
|
||||
|
||||
```python
|
||||
def exclusive_tracker_ips(pure_hosts: Iterable[str],
|
||||
resolve: Callable[[str], List[str]],
|
||||
allow_nets: Iterable) -> set:
|
||||
"""Resolve each pure-tracker host and return the set of IPs that are NOT in
|
||||
the CDN/cloud allowlist (so safe to nft-drop). `resolve` is injected
|
||||
(escalate._resolve_ips in production) to keep this function pure/testable."""
|
||||
allow = list(allow_nets)
|
||||
drop: set = set()
|
||||
for host in pure_hosts:
|
||||
if not host:
|
||||
continue
|
||||
try:
|
||||
ips = resolve(host) or []
|
||||
except Exception:
|
||||
ips = []
|
||||
for ip in ips:
|
||||
if ip and not ip_in_allowlist(ip, allow):
|
||||
drop.add(ip)
|
||||
return drop
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/test_ip_dns.py -q`
|
||||
Expected: PASS (5 passed)
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/secubox-toolbox/secubox_toolbox/ip_dns.py packages/secubox-toolbox/tests/test_ip_dns.py
|
||||
git commit -m "feat(toolbox): ip_dns exclusive-tracker-IP computation (CDN-gated) (ref #633)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: CDN allowlist data file
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/secubox-toolbox/data/cdn-allowlist.txt`
|
||||
- Modify: `packages/secubox-toolbox/debian/rules` (ship `data/`)
|
||||
|
||||
- [ ] **Step 1: Create the data file**
|
||||
|
||||
Create `packages/secubox-toolbox/data/cdn-allowlist.txt` with a documented header and a curated starter set of major CDN/cloud CIDRs. Use these real published ranges (a conservative starter subset — the file header documents manual refresh):
|
||||
|
||||
```text
|
||||
# SecuBox Anti-Track v2 (#633) — CDN / cloud IP allowlist.
|
||||
# IPs inside these ranges are NEVER nft-dropped even when a pure tracker
|
||||
# resolves there, to avoid blackholing shared infrastructure.
|
||||
# One CIDR per line; '#' comments. Manual refresh on release from each
|
||||
# provider's published ranges (Cloudflare/AWS/Google/Azure publish JSON;
|
||||
# Fastly/Akamai via their docs). Conservative starter set — extend as needed.
|
||||
|
||||
# --- Cloudflare (https://www.cloudflare.com/ips/) ---
|
||||
173.245.48.0/20
|
||||
103.21.244.0/22
|
||||
103.22.200.0/22
|
||||
103.31.4.0/22
|
||||
141.101.64.0/18
|
||||
108.162.192.0/18
|
||||
190.93.240.0/20
|
||||
188.114.96.0/20
|
||||
197.234.240.0/22
|
||||
198.41.128.0/17
|
||||
162.158.0.0/15
|
||||
104.16.0.0/13
|
||||
104.24.0.0/14
|
||||
172.64.0.0/13
|
||||
131.0.72.0/22
|
||||
2400:cb00::/32
|
||||
2606:4700::/32
|
||||
2803:f800::/32
|
||||
2405:b500::/32
|
||||
2405:8100::/32
|
||||
2a06:98c0::/29
|
||||
2c0f:f248::/32
|
||||
|
||||
# --- Fastly (https://api.fastly.com/public-ip-list) ---
|
||||
151.101.0.0/16
|
||||
199.232.0.0/16
|
||||
2a04:4e40::/32
|
||||
2a04:4e42::/32
|
||||
|
||||
# --- Akamai (representative; refresh from Akamai docs) ---
|
||||
23.32.0.0/11
|
||||
23.192.0.0/11
|
||||
104.64.0.0/10
|
||||
184.24.0.0/13
|
||||
|
||||
# --- Google (gstatic/googleusercontent/cloud; refresh from cloud.json) ---
|
||||
8.8.4.0/24
|
||||
8.8.8.0/24
|
||||
34.64.0.0/10
|
||||
35.190.0.0/17
|
||||
142.250.0.0/15
|
||||
172.217.0.0/16
|
||||
216.58.192.0/19
|
||||
2607:f8b0::/32
|
||||
2a00:1450::/32
|
||||
|
||||
# --- Amazon (CloudFront/AWS; refresh from ip-ranges.json) ---
|
||||
13.32.0.0/15
|
||||
13.224.0.0/14
|
||||
52.84.0.0/15
|
||||
54.182.0.0/16
|
||||
204.246.164.0/22
|
||||
2600:9000::/28
|
||||
|
||||
# --- Microsoft Azure (representative; refresh from ServiceTags JSON) ---
|
||||
13.64.0.0/11
|
||||
20.33.0.0/16
|
||||
40.64.0.0/10
|
||||
2603:1000::/24
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Confirm/extend the `debian/rules` ship line for `data/`**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && grep -n "data\|cp -r \|install -d" debian/rules | head`
|
||||
The package installs into `/usr/lib/secubox/toolbox/`. Add a step (in `override_dh_auto_install`, alongside the existing `cp -r secubox_toolbox …` / `cp -r conf …` lines) to ship the data dir:
|
||||
```make
|
||||
cp -r data debian/secubox-toolbox/usr/lib/secubox/toolbox/
|
||||
```
|
||||
(Match the surrounding indentation/style. This installs `data/cdn-allowlist.txt` → `/usr/lib/secubox/toolbox/data/cdn-allowlist.txt`, the default `ip_dns.CDN_ALLOWLIST_PATH`.)
|
||||
|
||||
- [ ] **Step 3: Verify the file parses**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -c "from secubox_toolbox import ip_dns; n=ip_dns.load_cdn_allowlist('data/cdn-allowlist.txt'); print(len(n), 'networks'); assert len(n) > 30"`
|
||||
Expected: prints a count > 30, no assertion error (confirms every non-comment line is a valid CIDR).
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/secubox-toolbox/data/cdn-allowlist.txt packages/secubox-toolbox/debian/rules
|
||||
git commit -m "feat(toolbox): ship CDN/cloud allowlist for exclusive-IP gate (ref #633)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: wire exclusive-IP drop into `escalate.py`
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/secubox-toolbox/secubox_toolbox/escalate.py`
|
||||
- Test: `packages/secubox-toolbox/tests/test_escalate_pure_ip.py`
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
```python
|
||||
# packages/secubox-toolbox/tests/test_escalate_pure_ip.py
|
||||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
from secubox_toolbox import escalate
|
||||
|
||||
|
||||
def test_pure_ip_drop_gated_off_when_not_enforcing(tmp_path, monkeypatch):
|
||||
pure = tmp_path / "pure.txt"; pure.write_text("ga.trk\n")
|
||||
dropped = []
|
||||
monkeypatch.setattr(escalate, "_resolve_ips", lambda h: ["203.0.113.9"])
|
||||
monkeypatch.setattr(escalate, "_nft_add_blacklist",
|
||||
lambda ip: dropped.append(ip) or True)
|
||||
# privacy_enforce False → no drops
|
||||
n = escalate.pure_tracker_ip_drop(
|
||||
pure_path=str(pure), allowlist_path=str(tmp_path / "none.txt"),
|
||||
enforce=False, ip_drop=True)
|
||||
assert n == 0 and dropped == []
|
||||
|
||||
|
||||
def test_pure_ip_drop_applies_non_cdn_only(tmp_path, monkeypatch):
|
||||
pure = tmp_path / "pure.txt"; pure.write_text("ga.trk\ncdn.trk\n")
|
||||
cdn = tmp_path / "cdn.txt"; cdn.write_text("104.16.0.0/13\n")
|
||||
dropped = []
|
||||
resolve = {"ga.trk": ["203.0.113.9"], "cdn.trk": ["104.16.1.2"]}
|
||||
monkeypatch.setattr(escalate, "_resolve_ips", lambda h: resolve.get(h, []))
|
||||
monkeypatch.setattr(escalate, "_nft_add_blacklist",
|
||||
lambda ip: dropped.append(ip) or True)
|
||||
monkeypatch.setattr(escalate, "_audit", lambda msg: None)
|
||||
n = escalate.pure_tracker_ip_drop(
|
||||
pure_path=str(pure), allowlist_path=str(cdn),
|
||||
enforce=True, ip_drop=True)
|
||||
assert dropped == ["203.0.113.9"] # CDN IP 104.16.1.2 excluded
|
||||
assert n == 1
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/test_escalate_pure_ip.py -q`
|
||||
Expected: FAIL — `AttributeError: module 'secubox_toolbox.escalate' has no attribute 'pure_tracker_ip_drop'`
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
In `escalate.py`, add the import near the top (after the existing `from typing import ...`):
|
||||
```python
|
||||
from . import ip_dns
|
||||
```
|
||||
Add the constant near `ESCALATE_TTL`:
|
||||
```python
|
||||
PURE_TRACKERS_PATH = os.environ.get(
|
||||
"SECUBOX_PURE_TRACKERS", "/var/lib/secubox/toolbox/pure-trackers.txt")
|
||||
```
|
||||
Add the function (after `_cscli_decision`, before `evaluate_and_apply`):
|
||||
```python
|
||||
def pure_tracker_ip_drop(pure_path: str = PURE_TRACKERS_PATH,
|
||||
allowlist_path: str = ip_dns.CDN_ALLOWLIST_PATH,
|
||||
enforce: bool = False, ip_drop: bool = False) -> int:
|
||||
"""Drop the IPs of confirmed pure-tracker domains into the nft blacklist,
|
||||
excluding CDN/cloud ranges. No-op unless enforce AND ip_drop. Returns the
|
||||
number of IPs dropped. Reversible (TTL) + audited."""
|
||||
if not (enforce and ip_drop):
|
||||
return 0
|
||||
try:
|
||||
hosts = [ln.split()[0].lower()
|
||||
for ln in Path(pure_path).read_text(encoding="utf-8").splitlines()
|
||||
if ln.strip() and not ln.strip().startswith("#")]
|
||||
except OSError:
|
||||
return 0
|
||||
nets = ip_dns.load_cdn_allowlist(allowlist_path)
|
||||
ips = ip_dns.exclusive_tracker_ips(hosts, _resolve_ips, nets)
|
||||
dropped = 0
|
||||
for ip in sorted(ips):
|
||||
if _nft_add_blacklist(ip):
|
||||
dropped += 1
|
||||
if dropped:
|
||||
_audit(f"ESCALATE pure-tracker-ip dropped={dropped} ttl={ESCALATE_TTL}")
|
||||
return dropped
|
||||
```
|
||||
Then wire it into the timer entry `evaluate_and_apply()`: near the top of that function (after `summary = {...}` is initialised — read the function to place it correctly), add a gated call reading the Plan-1 filters:
|
||||
```python
|
||||
# Anti-Track v2 (#633): pure-tracker exclusive-IP drop (dark unless armed).
|
||||
try:
|
||||
import sys as _sys
|
||||
_sys.path.insert(0, os.environ.get("SECUBOX_TOOLBOX_LIB",
|
||||
"/usr/lib/secubox/toolbox"))
|
||||
from secubox_toolbox.filters import get_filters as _gf
|
||||
_f = _gf()
|
||||
_n = pure_tracker_ip_drop(enforce=bool(_f.get("privacy_enforce")),
|
||||
ip_drop=bool(_f.get("privacy_ip_drop")))
|
||||
if _n:
|
||||
summary["actions"].append(f"pure-tracker-ip drop x{_n}")
|
||||
except Exception as e:
|
||||
log.warning("pure_tracker_ip_drop step failed: %s", e)
|
||||
```
|
||||
(Place this so a failure can't abort the existing escalation logic — wrap in its own try as shown. Adapt the `summary[...]` key to whatever the function actually returns; if `summary` has no `actions` list at that point, append to an existing list field or skip the summary mutation.)
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/test_escalate_pure_ip.py -q`
|
||||
Expected: PASS (2 passed). Then full suite: `python -m pytest tests/ -q`.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/secubox-toolbox/secubox_toolbox/escalate.py packages/secubox-toolbox/tests/test_escalate_pure_ip.py
|
||||
git commit -m "feat(toolbox): escalate drops exclusive pure-tracker IPs (dark-gated, CDN-safe) (ref #633)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: packaging + full gate
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/secubox-toolbox/debian/changelog`
|
||||
|
||||
- [ ] **Step 1: Confirm `ip_dns.py` + `data/` ship**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && grep -n "cp -r secubox_toolbox\|cp -r data" debian/rules`
|
||||
Expected: `secubox_toolbox` (covers `ip_dns.py`) and the `data` line added in Task 3 both present.
|
||||
|
||||
- [ ] **Step 2: Bump changelog**
|
||||
|
||||
Add a new top entry in `debian/changelog`, next version after `2.6.43-1~bookworm1` → `2.6.44-1~bookworm1`, author `Gerald KERMA <devel@cybermind.fr>`, dated 2026-06-17, dch format:
|
||||
```
|
||||
* Anti-Track v2 Plan 2b (#633): exclusive pure-tracker IP nft-drop via
|
||||
escalate.py, gated dark (privacy_enforce && privacy_ip_drop), with a
|
||||
shipped CDN/cloud allowlist (cdn-allowlist.txt) so shared-infra IPs are
|
||||
never blackholed. TTL'd + audited. DNS-refuse feed deferred to a follow-up.
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Full gate**
|
||||
|
||||
Run: `cd packages/secubox-toolbox && python -m pytest tests/ -q` (expect all green — ~47 tests).
|
||||
Run: `python -c "import ast; ast.parse(open('secubox_toolbox/ip_dns.py').read()); ast.parse(open('secubox_toolbox/escalate.py').read()); print('ok')"`.
|
||||
Run: `python -m pyflakes secubox_toolbox/ip_dns.py` if available (expect none); else skip.
|
||||
Run: `head -1 secubox_toolbox/ip_dns.py` (SPDX present).
|
||||
Run: `dpkg-parsechangelog -l debian/changelog | grep Version` (`2.6.44-1~bookworm1`).
|
||||
Run: `git status --short` (empty after commit).
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add packages/secubox-toolbox/debian/changelog
|
||||
git commit -m "chore(toolbox): changelog for Anti-Track v2 Plan 2b IP-drop (ref #633)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
**Spec coverage (this plan = §3 + §4; §5 DNS deferred):**
|
||||
- §3 CDN allowlist (static shipped, ipaddress parse, membership) → Tasks 1, 3. ✓
|
||||
- §4 exclusive-IP computation (pure source, CDN-gated, dedup) → Task 2. ✓
|
||||
- §4 escalate wiring (read pure-trackers, resolve via `_resolve_ips`, `_nft_add_blacklist` TTL, audit) → Task 4. ✓
|
||||
- §1/§2 dark gating (`privacy_enforce && privacy_ip_drop`, both default-ish off) → Task 4 (`pure_tracker_ip_drop` returns 0 unless both true; filters read in `evaluate_and_apply`). ✓
|
||||
- Packaging/ship → Tasks 3, 5. ✓
|
||||
- **§5 DNS-refuse feed → DEFERRED** to a separate plan (dns-guard owns blocklist.txt; integration mechanism needs the resolver topology confirmed). Recorded as out-of-scope here.
|
||||
|
||||
**Placeholder scan:** none — complete code in every code step; Step-2/Step-1 greps are explicit verifications. The one adaptive instruction (Task 4 Step 3 "adapt the summary key") is bounded with a concrete fallback ("skip the summary mutation").
|
||||
|
||||
**Type consistency:** `load_cdn_allowlist(path)->list`, `ip_in_allowlist(ip, networks)->bool`, `exclusive_tracker_ips(pure_hosts, resolve, allow_nets)->set`, `pure_tracker_ip_drop(pure_path, allowlist_path, enforce, ip_drop)->int` — consistent across Tasks 1/2/4 and tests. `CDN_ALLOWLIST_PATH` constant reused. `_resolve_ips`/`_nft_add_blacklist`/`_audit` are the real escalate.py names (verified).
|
||||
|
||||
**Rollout:** inert until `privacy_enforce=true` AND `privacy_ip_drop=true`. nft adds are TTL'd (4h, auto-renew per timer run) + audited; CDN allowlist is the collateral gate; pure-only source guarantees no load-bearing tracker is IP-dropped. No shared-dir mode changes.
|
||||
Loading…
Reference in New Issue
Block a user