Pre-publish privacy audit: scrub the simulator serial, harden the sdist, add licences

Audited the package against the two-stage procedure before a first PyPI
publish. The sdist and wheel were already tight, but three things needed
fixing and the controls needed to become real rather than documented.

The simulator volunteered high="PI04" as its product serial, which is the
real Bebop 2 serial prefix. Nothing unique to one aircraft, but a realistic
prefix invites being quoted into a bug report as a specimen, so it now reads
"N0TAREAL" / "0000000SIM" with a comment saying why it is nonsense on
purpose. The low half and the 500.0 no-fix GPS sentinel were already fake.

Hardened [tool.uv.build-backend] source-exclude well past the directories
that exist today: captures at any depth, log dumps, recorded media by
extension, caches, and anything credential-shaped. .gitignore governs git
and source-exclude governs the sdist; a capture can sit in one and not the
other, which is how this kind of data reaches an immutable index. Verified
the broad patterns do not over-reach: arsdk-xml/ with PROVENANCE.md and
tools/logs.py both still ship.

Added LICENSE (MIT) and LICENSE-arsdk-xml (Parrot SA's BSD-3-Clause), and
corrected the declared licence to "MIT AND BSD-3-Clause". The vendored XML
ships in both artifacts because nothing here decodes a command without it,
so MIT alone understated what is in the box. Both texts now appear in the
artifacts and in the metadata.

test_packaging.py grows privacy guards that fail on a serial prefix, a P7
CPU id, any MAC, a high-precision coordinate, an absolute home path, or any
private address other than the drone's own documented 192.168.42.0/24. Each
pattern was checked against the real identifiers to confirm it bites, since
a guard that passes on an empty tree proves nothing.

Example address in test_arsdk_session.py moved to RFC 5737 space.

504 tests pass, ruff clean.
This commit is contained in:
2026-10-03 11:21:13 -06:00
parent e9b0c264c5
commit c5e84c47ae
7 changed files with 265 additions and 11 deletions
+77 -2
View File
@@ -4,7 +4,11 @@ version = "2026.10.02"
description = "MCP server for the Parrot Bebop 2 drone: telemetry, camera, files, and every ARSDK command"
readme = "README.md"
requires-python = ">=3.12"
license = "MIT"
# Our own code is MIT. The vendored arsdk-xml/ is Parrot SA's, BSD-3-Clause,
# and it ships in both artifacts because the package cannot decode a single
# command without it, so the distribution as a whole is both.
license = "MIT AND BSD-3-Clause"
license-files = ["LICENSE", "LICENSE-arsdk-xml"]
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
keywords = ["mcp", "drone", "parrot", "bebop", "arsdk", "fastmcp"]
classifiers = [
@@ -35,7 +39,78 @@ requires = ["uv_build>=0.11.3,<0.12.0"]
build-backend = "uv_build"
[tool.uv.build-backend]
source-exclude = ["CLAUDE.md", ".env", ".env.*", ".mcp.json", "tests", "captures"]
# Err toward excluding. This repo sits next to a real aircraft, and `captures/`
# holds video of the owner's home plus `ckcm.bin` system logs that carry the
# serial, the CPU id and a real GPS fix together. None of it may ever reach an
# sdist, which is immutable and mirrored within minutes of upload.
#
# `.gitignore` does NOT protect this: it governs git, and source-exclude
# governs the sdist. A file can easily be in one and not the other, so the
# patterns below are deliberately broader than the directories that exist
# today -- a capture dropped into a new directory next year is still excluded.
# test_packaging.py asserts both halves stay in agreement.
source-exclude = [
# Operator-private context and anything credential-shaped.
"CLAUDE.md",
".env",
".env.*",
".mcp.json",
".claude",
".claude/**",
# Dev-only trees. Tests carry fixtures copy-pasted from a live aircraft.
"tests",
"tests/**",
"docs",
"docs/**",
"scripts",
"scripts/**",
"*.ipynb",
# Captures off the aircraft, at any depth, plus log dumps.
"captures",
"captures/**",
"**/captures/**",
"**/logs/**",
"ckcm*",
"**/ckcm*",
# Recorded media and binary blobs by extension, so a stray file in a
# directory nobody thought to list is still caught.
"*.rtpcap",
"*.raw",
"*.h264",
"*.bin",
"*.pcap",
"*.pcapng",
"*.mp4",
"*.jpg",
"*.jpeg",
"*.png",
"*.wav",
"*.ulg",
"**/*.rtpcap",
"**/*.raw",
"**/*.h264",
"**/*.bin",
"**/*.jpg",
"**/*.jpeg",
"**/*.png",
# Caches and build output. .pyc files embed the absolute build path, which
# leaks a username and the local directory layout.
"**/__pycache__",
"**/__pycache__/**",
"*.pyc",
"**/*.pyc",
".pytest_cache",
".pytest_cache/**",
".ruff_cache",
".ruff_cache/**",
".venv",
".venv/**",
"dist",
"dist/**",
"build",
"build/**",
"*.egg-info",
]
[dependency-groups]
dev = ["ruff>=0.16", "pytest>=8.0", "pytest-asyncio>=0.25"]