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
2 changes: 1 addition & 1 deletion .github/requirements/publish.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# What the two jobs that hold the PyPI token install: `publish` in stable.yml and `publish-dev` in dev.yml.
# Their version scripts (the inline bump in stable.yml, scripts/dev_release.py) use only the standard library.
# publish.txt is generated from this file:
# uv pip compile .github/requirements/publish.in --generate-hashes --python-version 3.12 --python-platform x86_64-manylinux_2_28 --only-binary :all: --exclude-newer 2026-09-24 -o .github/requirements/publish.txt
# uv pip compile .github/requirements/publish.in --generate-hashes --python-version 3.12 --python-platform x86_64-manylinux_2_28 --only-binary :all: --exclude-newer 2026-10-25 -o .github/requirements/publish.txt
# --python-version is the one both jobs set up. --exclude-newer leaves out the uploads of the last 7 days:
# move the date when regenerating.
build==1.5.0
Expand Down
14 changes: 7 additions & 7 deletions .github/requirements/publish.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# This file was autogenerated by uv via the following command:
# uv pip compile .github/requirements/publish.in --generate-hashes --python-version 3.12 --python-platform x86_64-manylinux_2_28 --only-binary :all: --exclude-newer 2026-09-24 -o .github/requirements/publish.txt
# uv pip compile .github/requirements/publish.in --generate-hashes --python-version 3.12 --python-platform x86_64-manylinux_2_28 --only-binary :all: --exclude-newer 2026-10-25 -o .github/requirements/publish.txt
build==1.5.0 \
--hash=sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f \
--hash=sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647
# via -r .github/requirements/publish.in
certifi==2026.7.22 \
--hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \
--hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55
certifi==2026.10.23 \
--hash=sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd \
--hash=sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1
# via requests
cffi==2.1.1 \
--hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \
Expand Down Expand Up @@ -455,9 +455,9 @@ secretstorage==3.5.0 \
--hash=sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137 \
--hash=sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be
# via keyring
setuptools==84.0.0 \
--hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 \
--hash=sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73
setuptools==84.2.1 \
--hash=sha256:3ff8b3b5492a0aa66960f4419365a86b84e013070d4313e4d4a6894f5d64d6bc \
--hash=sha256:ca140b45d5325a9478f880e351484f62fef15f336462e449ca0c03c873d589c2
# via -r .github/requirements/publish.in
twine==6.2.0 \
--hash=sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8 \
Expand Down
10 changes: 10 additions & 0 deletions Progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,13 @@ pip install --dry-run --only-binary=:all: --platform win_arm64 --python-version
TCC 權限都給)。`platform-smoke.yml` 的 `macos-capabilities` 只在推送到 `main` 與對 `main` 的 PR 上執行,所以下一個 dev→main
PR 才會確認 macos-15 是否給一樣的權限;若不同,依 `--measure` 重新實測後更新 `EXPECTED` 與
`docs/CAPABILITY_MATRIX.md`,不要直接放寬 gate。

---

## minimatch 11 在 VS Code 擴充套件

`BLOCKED` — `autocontrol-lsp/vscode/package-lock.json` 的 `minimatch` 5.1.9 有 Dependabot 警示
GHSA-g3hv-75wg-m249(moderate),只有 11.0.1 起修好(U-20261101-17)。它來自 `vscode-languageclient` 9.0.1,
會被 esbuild 打包進擴充套件;到 11.0.0 為止的每一版都要求 `minimatch` ^5 或 ^10,並呼叫 minimatch 11 已移除的
`Minimatch#makeRe()`,所以用 `overrides` 強制 11 會讓文件篩選在執行時壞掉。等 `vscode-languageclient`
出支援 minimatch 11 的版本,再升級並重新產生 lock。
2 changes: 1 addition & 1 deletion Third_Party_License.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Full copies of the upstream license texts are archived under `LICENSEs/`.
| Package | Version | License | Purpose |
|---|---|---|---|
| [je_open_cv](https://pypi.org/project/je-open-cv/) | 0.0.22 | MIT | OpenCV-based image recognition helpers (`locate_all_image`, template match) |
| [Pillow](https://pypi.org/project/Pillow/) | 12.2.0 | MIT-CMU (HPND) | Screenshot encoding, image I/O |
| [Pillow](https://pypi.org/project/Pillow/) | 13.0.0 (12.3.0 on Python 3.10) | MIT-CMU (HPND) | Screenshot encoding, image I/O |
| [mss](https://pypi.org/project/mss/) | 10.1.0 | MIT | Fast multi-monitor screenshot backend |
| [pyobjc-core](https://pypi.org/project/pyobjc-core/) | 12.1 | MIT | macOS backend — Python/Objective-C bridge *(Darwin only)* |
| [pyobjc](https://pypi.org/project/pyobjc/) | 12.1 | MIT | macOS backend — Cocoa / Quartz bindings *(Darwin only)* |
Expand Down
8 changes: 6 additions & 2 deletions dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ license-files = ["LICENSE"]
dependencies = [
"je_open_cv==0.0.22; sys_platform != 'win32' or platform_machine != 'ARM64'",
"opencv-python>=4.8,<6; sys_platform != 'win32' or platform_machine != 'ARM64'",
"pillow==12.3.0",
# Pillow 13.0.0 fixes the 12.x advisories (critical: GHSA-v9pv-5gc8-w2m6,
# GHSA-5x8w-8jj5-q52h) but needs Python >=3.11; 3.10 keeps 12.3.0, the
# newest release it can install.
"pillow==12.3.0; python_version < '3.11'",
"pillow==13.0.0; python_version >= '3.11'",
"pyobjc-core==12.2.2;platform_system=='Darwin'",
"pyobjc==12.2.2;platform_system=='Darwin'",
"python-Xlib==0.33;platform_system=='Linux' or platform_system=='FreeBSD' or platform_system=='OpenBSD' or platform_system=='NetBSD'",
Expand Down Expand Up @@ -88,7 +92,7 @@ gui = ["PySide6==6.11.2"]
webrtc = ["aiortc>=1.14.0", "av>=14.0.0"]
signaling = ["fastapi>=0.115", "starlette>=1.0.1", "uvicorn>=0.32"]
discovery = ["zeroconf>=0.149.16"]
pdf = ["pypdf>=6.16.1"]
pdf = ["pypdf>=6.21.4"]
office = ["openpyxl>=3.1", "python-docx>=1.1", "python-pptx>=0.6"]
fuzzy = ["rapidfuzz>=3.0"]
s3 = ["boto3>=1.34"]
Expand Down
40 changes: 40 additions & 0 deletions docs/updates/2026-11.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,3 +519,43 @@ Index and query commands: [README.md](README.md). New entries go at the end.
round that had ignored that thread (see U-20261031-04 and
U-20261101-15).
- **Open items:** none.

## U-20261101-17 · 2026-11-01 · Patch the dependencies named in Dependabot security alerts · #bugfix #deps #security #ci

- **What:** cleared the Dependabot security alerts that an update can fix
today.
- `pyproject.toml`, `dev.toml`: `pillow==12.3.0` becomes
`pillow==12.3.0; python_version < '3.11'` and
`pillow==13.0.0; python_version >= '3.11'` (41 advisories, critical
among them GHSA-v9pv-5gc8-w2m6 and GHSA-5x8w-8jj5-q52h). Pillow 13 needs
Python ≥3.11, so 3.10 keeps the newest release it can install. The `pdf`
extra's security floor moves from `pypdf>=6.16.1` to `pypdf>=6.21.4`, the
first release without the 6.20–6.21 advisories.
- `uv.lock`: `pillow` 13.0.0 on 3.11+, `pypdf` 6.19.0 → 6.21.4, and
`dnspython` 2.8.0 → 2.9.0 on 3.11+ (it needs 3.11 too). The `dnspython`
floor is a `[tool.uv] constraint-dependencies` entry in `pyproject.toml`;
it changes only the lock.
- `.github/requirements/publish.txt`: `setuptools` 84.0.0 → 84.2.1,
`certifi` 2026.7.22 → 2026.10.23; `publish.in` records the cut-off
`--exclude-newer 2026-10-25`, still a 7-day wait.
- `Third_Party_License.md` names the Pillow versions now pinned.
- **How:** each lock regenerated with its recorded command plus `-P` for the
patched packages only; every other pin is kept. `pypdf` 6.21.4
(2026-10-28) is the first fixed release and is newer than the cut-off.
- **Not changed:** `autocontrol-lsp/vscode/package-lock.json` keeps
`minimatch` 5.1.9 (GHSA-g3hv-75wg-m249, fixed only in 11.0.1). It comes from
`vscode-languageclient`, and every release through 11.0.0 calls
`Minimatch#makeRe()`, which minimatch 11 removed.
- **Test:** `test_arm64_dependency_markers.py` read exactly one Pillow line
and failed on the split. It now reads every line of a package and checks
that none names the platform (`sys_platform`, `platform_machine`,
`platform_system`, `os_name`); a Python-version marker passes. A new case
checks that pattern, and putting the arm64 marker on Pillow still fails it.
- **Evidence:** the full headless suite (`python -m pytest`, as in
`dev.yml`) in a Python 3.12 environment with Pillow 13.0.0, pypdf 6.21.4
and dnspython 2.9.0: 16711 passed, 191 skipped, and the one failure was
that test, fixed here (its file, the dev.toml parity and publish lock tests:
32 passed).
- **Downstream:** TestPioneer and WebRunner lock Pillow through
`je_auto_control`; they move to 13.0.0 once a release carries this pin.
- **Open items:** `Progress.md`, "minimatch 11 在 VS Code 擴充套件".
3 changes: 2 additions & 1 deletion docs/updates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ In the same commit: delete the item from `Progress.md`, add a `#done` entry here

| ID | Date | Title | Tags | Batch |
|---|---|---|---|---|
| U-20261101-17 | 2026-11-01 | Patch the dependencies named in Dependabot security alerts | #bugfix #deps #security #ci | [2026-11](2026-11.md) |
| U-20261101-16 | 2026-11-01 | Accept either refusal when a revocation races the lease watch | #incident #ci #test | [2026-11](2026-11.md) |
| U-20261101-15 | 2026-11-01 | Observe only the maintenance call's own writes in the receipt test | #incident #ci #test | [2026-11](2026-11.md) |
| U-20261101-14 | 2026-11-01 | Carry the listener's pid namespace in the Linux endpoint | #incident #ci #linux | [2026-11](2026-11.md) |
Expand Down Expand Up @@ -573,7 +574,7 @@ In the same commit: delete the item from `Progress.md`, add a `#done` entry here

| File | Period | Entries |
|---|---|---:|
| [2026-11.md](2026-11.md) | 2026-11 | 16 |
| [2026-11.md](2026-11.md) | 2026-11 | 17 |
| [2026-10-f.md](2026-10-f.md) | 2026-10 | 5 |
| [2026-10-e.md](2026-10-e.md) | 2026-10 | 10 |
| [2026-10-d.md](2026-10-d.md) | 2026-10 | 15 |
Expand Down
19 changes: 15 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ dependencies = [
# je_open_cv leaves opencv-python unpinned; bound the major so a new
# OpenCV release cannot silently change cv2 return shapes under us.
"opencv-python>=4.8,<6; sys_platform != 'win32' or platform_machine != 'ARM64'",
"pillow==12.3.0",
# Pillow 13.0.0 fixes the 12.x advisories (critical: GHSA-v9pv-5gc8-w2m6,
# GHSA-5x8w-8jj5-q52h) but needs Python >=3.11; 3.10 keeps 12.3.0, the
# newest release it can install.
"pillow==12.3.0; python_version < '3.11'",
"pillow==13.0.0; python_version >= '3.11'",
"pyobjc-core==12.2.2;platform_system=='Darwin'",
"pyobjc==12.2.2;platform_system=='Darwin'",
# The BSDs run the same X11 stack, and platform_system is the kernel
Expand Down Expand Up @@ -114,15 +118,22 @@ webrtc = ["aiortc>=1.14.0", "av>=14.0.0"]
signaling = ["fastapi>=0.115", "starlette>=1.0.1", "uvicorn>=0.32"]
discovery = ["zeroconf>=0.149.16"]
# Security floor: utils/pdf opens PDFs the caller hands it, and every pypdf
# before 6.16.1 has a malformed-file infinite loop or memory blow-up
# (inline images, xref entries, /ToUnicode, CID widths, outlines, XForms).
pdf = ["pypdf>=6.16.1"]
# before 6.21.4 has a malformed-file infinite loop, long runtime or memory
# blow-up (xref tables and streams, page trees, outlines, form fields,
# content streams, encrypted files, /ToUnicode, CID widths, XForms).
pdf = ["pypdf>=6.21.4"]
office = ["openpyxl>=3.1", "python-docx>=1.1", "python-pptx>=0.6"]
fuzzy = ["rapidfuzz>=3.0"]
s3 = ["boto3>=1.34"]
locale = ["babel>=2.12"]
audio = ["pycaw>=20240210"]

# Lock-only security floor for a transitive dependency (uv.lock; the wheel
# metadata is unchanged). dnspython 2.9.0 fixes the advisory in 2.8.0 but
# needs Python >=3.11, so 3.10 keeps 2.8.0.
[tool.uv]
constraint-dependencies = ["dnspython>=2.9.0; python_version >= '3.11'"]

# CLAUDE.md's 120-character line limit. Without this table CI's `ruff check`
# ran the default rule set, which has no E501, so the documented limit was
# enforced by nothing. Lines ending in a pragma (`# noqa`, `# nosec`) are
Expand Down
42 changes: 32 additions & 10 deletions test/unit_test/headless/test_arm64_dependency_markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
# purpose: it was once named a blocker and never was one.
UNMARKED = ("pillow", "mss", "defusedxml")

# A marker that names the platform. A Python-version marker is not one.
_PLATFORM_MARKER = re.compile(r"\b(sys_platform|platform_machine|platform_system|os_name)\b")

ENVIRONMENTS: Dict[str, Dict[str, str]] = {
"windows arm64": {
"sys_platform": "win32", "platform_machine": "ARM64",
Expand Down Expand Up @@ -85,14 +88,19 @@ def _dependencies() -> List[str]:
return found


def _requirement(name: str) -> str:
"""Return the single requirement string whose distribution is ``name``."""
def _requirements(name: str) -> List[str]:
"""Return every requirement string whose distribution is ``name``."""
prefix = name.lower()
matches = [
return [
text for text in _dependencies()
if text.lower().split(";")[0].strip()
.replace("_", "-").startswith(prefix.replace("_", "-"))
]


def _requirement(name: str) -> str:
"""Return the single requirement string whose distribution is ``name``."""
matches = _requirements(name)
assert len(matches) == 1, f"{name}: expected one requirement, got {matches}"
return matches[0]

Expand All @@ -115,13 +123,27 @@ def test_the_unavailable_wheels_are_marked_off_windows_arm64(name: str) -> None:

@pytest.mark.parametrize("name", UNMARKED)
def test_the_available_wheels_are_not_marked_off_anything(name: str) -> None:
"""Pillow, mss and defusedxml ship for arm64 and must stay unconditional."""
requirement = _requirement(name)
assert ARM64_MARKER not in requirement, (
f"{name} was marked off Windows arm64, but it publishes a win_arm64 "
f"wheel and was never a blocker. Marking it costs that platform a "
f"working feature for nothing."
)
"""Pillow, mss and defusedxml ship for arm64 and carry no platform marker.

A requirement may be split by Python version (Pillow 13 needs 3.11, so
3.10 keeps 12.3.0), but no line may depend on the platform.
"""
requirements = _requirements(name)
assert requirements, f"{name} is missing from the dependencies"
for requirement in requirements:
marker = requirement.split(";", 1)[1] if ";" in requirement else ""
assert not _PLATFORM_MARKER.search(marker), (
f"{name} was marked off a platform ({requirement!r}), but it "
f"publishes a win_arm64 wheel and was never a blocker. Marking it "
f"costs that platform a working feature for nothing."
)


def test_a_platform_marker_on_an_unmarked_package_is_caught() -> None:
"""The check above sees platform names and lets Python versions through."""
assert _PLATFORM_MARKER.search(ARM64_MARKER)
assert _PLATFORM_MARKER.search("platform_system=='Darwin'")
assert not _PLATFORM_MARKER.search("python_version >= '3.11'")


@pytest.mark.parametrize("label,environment", sorted(ENVIRONMENTS.items()))
Expand Down
Loading
Loading