Add Phase 9 validation and advisory review
Record the finite dependency search, Wi-Fi maintenance blocker, and pinned icon provenance. Add bounded host orchestration and fixture coverage, and update release documentation with current evidence.
This commit is contained in:
@@ -14,7 +14,7 @@ ESP32-S3 firmware for a secure, multi-transport RS-232 adapter. It operates one
|
||||
|
||||
## Development status
|
||||
|
||||
Hardware characterization, serial/USB/Wi-Fi/HTTPS/SSH and local display/control are implemented and hardware-validated. **Phase 8 role-based users and administration is complete:** 8A–8C were target-hardware validated and the user explicitly signed off tested firmware at **8D.22 (2026-09-13)**. See the [roadmap](docs/roadmap.md#phase-8--role-based-users-and-administrative-access--complete) and [acceptance evidence](docs/web_administration_acceptance.md). Very low internal/DMA lifetime minima remain a nonblocking headroom follow-up, not an approved reserve. **Phase 9 security hardening is in progress**: 9A crash/debug policy, 9B SSH admission/credential handling and 9C library cleanup/protocol policy have passed host/build checks. **9D maintenance and lifecycle is in progress:** an operator runbook, finite SSH parser/caller and IDF applicability reviews, and offline notice assembly tooling are available. Scoped corrections have parent host/build evidence; broader advisory coverage, release delivery/source/legal questions and whole-phase target gates remain open; **Phase 9 is not complete or production-ready**. The user will validate Phase 9 as a whole; no 9D device validation or license clearance is claimed. See [security hardening](docs/security_hardening.md) for scope, operational profiles, and validation gates.
|
||||
Hardware characterization, serial/USB/Wi-Fi/HTTPS/SSH and local display/control are implemented and hardware-validated. **Phase 8 role-based users and administration is complete:** 8A–8C were target-hardware validated and the user explicitly signed off tested firmware at **8D.22 (2026-09-13)**. See the [roadmap](docs/roadmap.md#phase-8--role-based-users-and-administrative-access--complete) and [acceptance evidence](docs/web_administration_acceptance.md). Very low internal/DMA lifetime minima remain a nonblocking headroom follow-up, not an approved reserve. **Phase 9 security hardening is in progress**: 9A crash/debug policy, 9B SSH admission/credential handling and 9C library cleanup/protocol policy have passed host/build checks. **9D maintenance and lifecycle is in progress:** an operator runbook, finite SSH parser/caller and IDF applicability reviews, and offline notice assembly tooling are available. Scoped corrections have parent host/build evidence; the [finite dependency search](docs/dependency_advisory_coverage.md) is complete (all 22 IDF advisory entries across three pages plus named managed/browser channels), but fresh findings, explicit coverage gaps, release delivery/source/legal questions and whole-phase target gates remain open. **The PMF-capable SoftAP security blocker is not fixed**: the [update plan](docs/wifi_security_update_plan.md) requires a coherent vendor framework/source/header/binary ABI bundle and compatible tooling, not PMF/WPA3 weakening; **Phase 9 is not complete or production-ready**. The user will validate Phase 9 as a whole; no 9D device validation or license clearance is claimed. See [security hardening](docs/security_hardening.md) for scope, operational profiles, and validation gates.
|
||||
|
||||
Latest supplied parent integration: `pio run` **PASS**, **94,340 B linked RAM / 1,768,901 B flash** (+200 B flash); **seven focused suites PASS**. See [exact counts and evidence limits](docs/security_hardening.md#ordering-hostbuild-evidence--2026-09-16). Linked size and host passes are not target headroom or release acceptance.
|
||||
|
||||
@@ -40,6 +40,8 @@ Keep UART0 ready for administrative recovery and native USB for network-independ
|
||||
- [wolfSSL/wolfSSH advisory review](docs/wolf_security_review.md): implemented scoped mitigations, historical research and remaining target/maintenance gates.
|
||||
- [Remaining SSH parser review](docs/ssh_parser_remaining_review.md): completed finite server-profile dispatch/caller review, corrections and explicit exclusions; not exhaustive parser safety.
|
||||
- [IDF applicability review](docs/idf_security_review.md): six named findings and a bounded advisory-index feature screen, not all-CVE clearance.
|
||||
- [Bounded Phase 9 host validation](docs/phase9_validation.md): [orchestrator](tools/validate_phase9.py), 23 default commands; build and OpenSSH interop are separate opt-ins.
|
||||
- [Exact icon provenance](docs/icon_provenance.md): official 7.4.47 retained SVGs verified; manual bitmap derivation and distinct mockup Wi-Fi path remain unresolved.
|
||||
- [Release notice packaging](docs/release_packaging.md): offline [notice tool](tools/release_notices.py), deterministic bounded assembly and separate recipient/source/legal gates.
|
||||
- [Command reference](docs/command_reference.md): UART0/admin-SSH administration, serial, broker, USB, Wi-Fi, mDNS, web, SSH, and diagnostic commands.
|
||||
|
||||
|
||||
@@ -26,12 +26,14 @@ This is a semantic map, not a complete file inventory. Start here, then read the
|
||||
- Latest supplied parent `pio run` PASS: **94,340 B linked RAM / 1,768,901 B flash (+200 B versus 1,768,701 B)**. Final parent **all seven suites PASS**: ordering `--interop` (8,028 checks / seven rejected mutations / 12 exact-256-KiB sessions, clean channel close and transport EOF), SDK overrides `--build-dir .pio/build/esp32-s3-devkitc-1-n16r8`, auth (135), protocol, strict crypto, notices (30), and parser (3,258 × two modes plus channel profiles; 11 + 18 + 2 rejected mutations). Interop used unsandboxed approval only for local AF_UNIX sockets; no remote network/device operation. Review's misplaced EOF guard is corrected in `SendChannelEof` before lookup/serialization/state mutation; follow-up verification found no scoped blocker. Not target/runtime-reserve evidence.
|
||||
- Restricted **CVE-2025-14942** ordering correction implemented: `tools/wolfssh_order/delta.json` supplies exact executable edits to `internal.c`, `ssh.c` and `internal.h`; [README](../../tools/wolfssh_order/README.md) records PR793/819/840/855/921 prerequisite disposition, and `provenance.json` pins archived patch hashes/commit IDs. Existing X25519/P-256 KEX only; independent SELF/PEER bits, expected-message/auth gates and exactly-once queued NEWKEYS under WANT_WRITE. **EXT_INFO intentionally disabled, no `server-sig-algs`; `extInfoSent` stays zero.** No full upstream backport or dependency upgrade. CMake propagates the generated ABI header BEFORE PUBLIC plus PUBLIC forced include with stale-header rejection; joined `-include/path` flags preserve ordering/crypto guards through PlatformIO sorting/deduplication.
|
||||
- Ordering tests: [tests/wolfssh_order_contract](../../tests/wolfssh_order_contract/README.md), full generated C/real wolfCrypt, **8,028 checks / seven rejected mutations**; `pio_adapter.py` validates installed PlatformIO/SCons flags with a real Xtensa consumer. Initial interop failed a harness close race despite early PASS output; fixed harness awaits peer close and transport EOF and independently reaps the server via fd-passing proxy. Final agent `--interop --interop-repeat 3`: **36/36 sessions**, exact **256 KiB** each, **two or ten** key exchanges and clean client/server exits. [Remaining gates](../wolf_security_review.md#restricted-ordering-correction-and-remaining-gates): target cleanup during rekey, no-EXT_INFO client compatibility, resource/timing and whole-phase validation; no device operation or phase sign-off.
|
||||
- [Finite dependency advisory snapshot](../dependency_advisory_coverage.md): all 22 IDF index entries across three pages plus named managed/browser channels accounted for; declared search complete, not exhaustive clearance. **PMF-capable SoftAP security blocker remains unfixed**; [Wi-Fi update plan](../wifi_security_update_plan.md) requires coherent vendor framework/toolchain/source/header/binary ABI bundles and override revalidation, not PMF/WPA3 weakening. Residual applicability questions and target gates remain.
|
||||
- [Bounded Phase 9 runner](../../tools/validate_phase9.py), [usage/evidence](../phase9_validation.md), fixtures `tests/phase9_validation/run.py`: 23 default commands; build and OpenSSH interop are independent opt-ins. After the owning agent fixed the stale console-boundary anchor, final supplied parent `CCACHE_DISABLE=1 python3 -B tools/validate_phase9.py` **PASS 23/23** and orchestrator fixtures **PASS 10 tests**. No build/interop this turn; historical build/interop evidence above is separate, not phase acceptance.
|
||||
- Policy/evidence/limits: [library review](../security_library_review.md), [Phase 9C](../security_hardening.md#9c-library-cleanup-and-protocol-policy). Source-contract tests must locate and verify actual generated compilation inputs, not assume original vendor paths.
|
||||
|
||||
## Offline release notice assembly
|
||||
|
||||
- [Procedure and release gates](../release_packaging.md), [tool](../../tools/release_notices.py), catalog `third_party/release-notices/inputs.json`, scope/TLSF provenance under `third_party/release-notices/`; tests: `tests/release_notices/run.py` (parent 30 PASS). 62 explicit hash/size-pinned inputs; no recursive collection, fetch, build, asset regeneration or device access; output must be fresh and outside input roots.
|
||||
- Supplied independent review: no actionable scoped parser/bundle defects; two actual 62-input bundles deterministic. Historical actual bundle: 64 files / 541,147 bytes, not remeasured here. The catalog does not pin these overview/review documents. Assembly is implemented; recipient delivery, corresponding source, legal/radio-blob basis, icon provenance and whole-phase target gates remain open. Do not equate a notice manifest with a source archive, complete SBOM or release clearance.
|
||||
- [Procedure and release gates](../release_packaging.md), [tool](../../tools/release_notices.py), catalog `third_party/release-notices/inputs.json`, scope/TLSF provenance under `third_party/release-notices/`; tests: `tests/release_notices/run.py` (supplied agent 36 fixture tests PASS). 75 explicit hash/size-pinned inputs; no recursive collection, fetch, build, asset regeneration or device access; output must be fresh and outside input roots.
|
||||
- Supplied agent evidence: two actual 75-input bundles verified deterministic, each **77 files / 4,433,930 bytes**; no reassembly here. [Exact retained SVG provenance](../icon_provenance.md) resolves official 7.4.47 sources at `9e04201d4557e729822fb57f62a316c3dea1d4a8` (Google USB, Simran Wi-Fi); manual bitmap derivation and distinct mockup Wi-Fi path remain unresolved. The catalog pins `docs/icon_provenance.md`, not this code map or the edited overview docs; coordinate changes to pinned evidence, never silently repin. Recipient delivery, corresponding source, legal/radio-blob basis and whole-phase target gates remain open. A notice manifest is not a source archive, complete SBOM or release clearance.
|
||||
|
||||
## Secure randomness
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
Working memory, not an implementation timeline. Source is authoritative; begin with [code map](code-map.md), then [architecture](architecture.md) and [decisions](design-decisions.md).
|
||||
|
||||
## Phase 9D continuation — broader snapshot / reproducible validation — 2026-09-16
|
||||
|
||||
- Initial Git clean. `docs/dependency_advisory_coverage.md` completes finite dated search of all22official IDF advisories/3pages plus specified managed/browser channels. **New current-profile blocker:** PMF-capable mixed WPA2/WPA3 SoftAP matches vendor management-frame memory-corruption path; installed7S3WiFi archives match original5.5. No exploit demonstrated. `docs/wifi_security_update_plan.md` verifies fix/release bundle differences and both callback uint8_t*→size_t* ABI changes. Verified reference IDF5.5.3 commit2c211b236707889e8400c4dc5644dd5c4ee071e0 contains fix; not deployment approval or automatic recommendation over newer releases. Do not C-only patch/swap one archive/disable PMF as guessed mitigation. Next coherent framework/toolchain/source/header/radio update evaluation with audited override rebase; PHY/coexistence/privateABI compatibility unresolved. Direct SDK AES-DMA/PSRAM applicability remains explicit review gap.
|
||||
- `tools/validate_phase9.py` fixed23command host plan, optional --build/--interop, strict existing build inputs, timeouts/process cleanup, no installs/device commands/permanent log captures. New10fixture tests. Actual run revealed stale admin_console_boundary extraction ending at removed ssh_malloc; replaced with reviewed consume_external_close endpoint and eight unique ordered markers, all existing assertions retained.
|
||||
- **Final parent default validation23/23PASS and orchestrator fixtures10PASS.** Default intentionally skips build/interop; no firmware rebuild or new device/interoperability claim this slice. Prior firmware94340RAM1768901flash remains historical. Additional boundary accounts/lifecycle/psram suites passed agent. Run full opt-ins only deliberately with prerequisites.
|
||||
- Exact retained USB/WiFi SVGs verified byte-identical official@mdi/svg7.4.47 commit9e04201d4557e729822fb57f62a316c3dea1d4a8; archive source/license/metadata/tree hashes in third_party/material-design-icons/upstream-7.4.47 and docs/icon_provenance.md. USB Google/WiFi Simran; fullApache text present. Mockup WiFi differs and firmware bitmap manual derivation not mechanically proven. Notice catalog75inputs preserving oldinputs except editedscopeREADME pins,36testsPASS; two actual agent bundles77files4433930bytes deterministic. No assets regenerated.
|
||||
- Globaldocs/code map reflect fresh blocker, finite review boundaries and final parent results. Remaining release/source/radiolegal/wolf packaging/bitmap preferred-source delivery questions not cleared. Whole-phase hardware still deferred; secureboot/encryptedNVS excluded. No upgrades/device operations/commits. Continue concrete WiFi correction evaluation, not repeat completed finite snapshots.
|
||||
|
||||
## Phase 9D continuation — deferred applicability / notice tooling — 2026-09-16
|
||||
|
||||
- Initial Git clean. `tools/security_overrides.py` now requires exact ssh-userauth service, exact bounded CHANNEL_FAILURE recipient with existing fatal policy, and length-first exact names for all nine channel requests. Supported bodies and unknown-request ACK/consumption retained. No ordering/crypto/version changes. `docs/ssh_parser_remaining_review.md` closes finite PR899/902/918/919 and generic current signature caller trace: client key-skip functions remain unpatched but blocked by current server ordering; forwarding absent; no current short-digest/OID trigger found. Not exhaustive parser certification.
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
# Broader dependency advisory coverage
|
||||
|
||||
Review date: **2026-09-16**. Documentation-only, finite snapshot; **not security certification, upgrade approval, or Phase 9 sign-off**.
|
||||
|
||||
## Result and authority
|
||||
|
||||
**New actionable maintenance finding:** the installed IDF 5.5 Wi-Fi bundle predates the official 5.5.3 correction for management-frame memory corruption. The project's PMF-capable WPA2/WPA3 SoftAP supplies the relevant supported configuration. Prioritize a coherent vendor-bundle update/backport investigation; see the exact evidence and limitations below. This is a current-feature exposure candidate supported by the vendor's release/fix description, **not a demonstrated device exploit or independently audited binary implementation**.
|
||||
|
||||
No additional current-path security correction was established for `esp_tinyusb`, TinyUSB, mDNS, LED strip, or the two vendored browser packages in this finite search. That statement does not establish that these dependencies are vulnerability-free.
|
||||
|
||||
- [IDF security review](idf_security_review.md) remains authoritative for its detailed mbedTLS findings, DHCP-server/EMS/X.509 backports, WebSocket subprotocol exclusion, and first-page feature dispositions. This document completes the previously omitted **IDF advisory-index pages**, without repeating those audits or claiming a fresh override verification.
|
||||
- [Wolf security review](wolf_security_review.md), [key-validation review](ssh_key_validation_review.md), and [remaining parser review](ssh_parser_remaining_review.md) retain their scopes and outstanding gates. Wolf advisories were not re-searched here.
|
||||
- Only `docs/dependency_advisory_coverage.md` was created. No sources, pins, manifests, assets, generated outputs, configuration, builds, tests, or devices were changed/run. Existing build metadata was read, not regenerated; it does not establish executable freshness or device state.
|
||||
|
||||
## Baseline actually inspected
|
||||
|
||||
Managed manifests were read from installed component directories, not inferred from the application's semver constraints. The application requests `mdns: ^1.8.2`, but installed mDNS is **1.12.0**.
|
||||
|
||||
| Dependency | Installed/source identity | Current use |
|
||||
| --- | --- | --- |
|
||||
| ESP-IDF | Existing reviewed 5.5.0 baseline; additional original SDK sources and Wi-Fi archives inspected below | Wi-Fi AP/STA, lwIP, HTTPS; existing local security overrides remain part of effective inputs |
|
||||
| `espressif/esp_tinyusb` | **2.2.1**, manifest commit `8e779566ef71d43928cbf7e125e8eb54bab3f542`, path `device/esp_tinyusb` | CDC device wrapper |
|
||||
| `espressif/tinyusb` | **0.21.0~1**, manifest commit `7049c58a0e895acc92c6407574b05b5536eddfc8` | Espressif fork, not interchangeable with an unqualified upstream 0.21.0 claim |
|
||||
| `espressif/mdns` | **1.12.0**, manifest commit `db06b19b7be729c163d346f62ec0eba01047b7f1` | STA hostname responder; no registered DNS-SD services or browser/query application calls |
|
||||
| `espressif/led_strip` | **3.0.3**, manifest commit `7cd447361ca2f0a1c01aa3089e3031f6171b6c7e` | One WS2812, RMT, no DMA |
|
||||
| `@xterm/xterm` | **5.5.0** per [asset provenance](../web_assets/SOURCES.md); local JS SHA-256 `1f991ac3b4b283ebf96e60ae23a00a52765dd3a2e46fa6fdda9f1aab032f7495` rechecked | Serial and admin browser terminals |
|
||||
| `@xterm/addon-fit` | **0.10.0**; local JS SHA-256 `bdaefa370b1bfc42ee88d46fe6072400902a4d4b2d45cd93438dda9b23c97089` rechecked | Only loaded addon |
|
||||
|
||||
Browser version attribution is to the recorded exact-package provenance plus matching local hashes. No npm install, registry dependency resolution, asset download, decompression, or embedded-asset regeneration was performed. Generated compressed arrays were not independently re-hashed in this slice.
|
||||
|
||||
Local configuration evidence is `.pio/build/esp32-s3-devkitc-1-n16r8/config/sdkconfig.h`; compilation evidence is a filtered read of the existing compilation database. Source references under `components/` below are relative to `/home/mscholz/.platformio/packages/framework-espidf/`.
|
||||
|
||||
## Finite search ledger: URLs checked and stopping rules
|
||||
|
||||
The search ended at the following dated snapshot. Empty official repository advisories mean **no published advisories returned by that endpoint**, not no CVEs anywhere, no undisclosed defects, or a security guarantee. GitHub releases and component changelogs are different publication channels; an empty Releases list is not evidence that component versions stopped shipping.
|
||||
|
||||
### IDF advisory index: all snapshot pages
|
||||
|
||||
Fetched the official HTML index [page 1](https://github.com/espressif/esp-idf/security/advisories), [page 2](https://github.com/espressif/esp-idf/security/advisories?page=2), and [page 3](https://github.com/espressif/esp-idf/security/advisories?page=3): **10 + 10 + 2 = 22 entries**, oldest June 23, 2022, newest September 3, 2026. Page 3 is the terminal listed page. Full advisory descriptions were read from the official [repository advisory API](https://api.github.com/repos/espressif/esp-idf/security-advisories?per_page=100&page=1), not merely titles.
|
||||
|
||||
The [API page=2 request](https://api.github.com/repos/espressif/esp-idf/security-advisories?per_page=100&page=2) unexpectedly returned the same 22 entries with no Link header. **It was not treated as an empty-page proof or 22 additional advisories.** The HTML pagination and unique IDs establish the 22-entry snapshot boundary. No unbounded pagination or general web search followed.
|
||||
|
||||
### Other official advisory channels
|
||||
|
||||
Each URL below returned the stated number of public advisory records, with no next-page Link header:
|
||||
|
||||
| Official endpoint checked | Records | Disposition |
|
||||
| --- | ---: | --- |
|
||||
| [esp-usb](https://api.github.com/repos/espressif/esp-usb/security-advisories?per_page=100&page=1) | 3 | HID/UVC **host** issues; device CDC exclusion below |
|
||||
| [hathach/tinyusb](https://api.github.com/repos/hathach/tinyusb/security-advisories?per_page=100&page=1) | 0 | No published records returned |
|
||||
| [espressif/tinyusb](https://api.github.com/repos/espressif/tinyusb/security-advisories?per_page=100&page=1) | 0 | Fork checked separately |
|
||||
| [esp-protocols](https://api.github.com/repos/espressif/esp-protocols/security-advisories?per_page=100&page=1) | 0 | mDNS release fixes screened separately |
|
||||
| [idf-extra-components](https://api.github.com/repos/espressif/idf-extra-components/security-advisories?per_page=100&page=1) | 0 | LED changelog screened separately |
|
||||
| [xterm.js](https://api.github.com/repos/xtermjs/xterm.js/security-advisories?per_page=100&page=1) | 0 | Browser release fixes screened separately |
|
||||
|
||||
### Release/changelog boundary
|
||||
|
||||
- **IDF:** [release API page 1](https://api.github.com/repos/espressif/esp-idf/releases?per_page=100&page=1) returned 100 records; [page 2](https://api.github.com/repos/espressif/esp-idf/releases?per_page=100&page=2) returned 64 and no additional 5.5 tags. Screened published stable **5.5.1–5.5.5** GitHub release bodies for explicit security, vulnerability, overflow, out-of-bounds, corruption, UAF and double-free descriptions; followed the relevant additional candidates below. This is not a line-by-line audit of every bugfix or another IDF branch. [5.5.4 body](https://api.github.com/repos/espressif/esp-idf/releases/tags/v5.5.4) mainly describes a NimBLE regression. The linked full [5.5.4](https://release-notes.espressif.tools/release/5.5.4) and [5.5.5](https://release-notes.espressif.tools/release/5.5.5) release databases returned **no textual content** through the fetch tool: their database-only contents are an explicit coverage gap, not reviewed pages.
|
||||
- **esp-usb:** [Releases API](https://api.github.com/repos/espressif/esp-usb/releases?per_page=100&page=1) returned zero. Read the official [component changelog](https://raw.githubusercontent.com/espressif/esp-usb/master/device/esp_tinyusb/CHANGELOG.md) through **2.3.0**, and [current manifest](https://api.github.com/repos/espressif/esp-usb/contents/device/esp_tinyusb/idf_component.yml), also 2.3.0. This establishes a changelog ceiling, not proof of registry publication or compatibility.
|
||||
- **TinyUSB:** [upstream Releases API](https://api.github.com/repos/hathach/tinyusb/releases?per_page=100&page=1) returned 18 records, newest **0.21.0**, whose full release notes were read. [Fork Releases API](https://api.github.com/repos/espressif/tinyusb/releases?per_page=100&page=1) returned zero. [Installed fork commit API](https://api.github.com/repos/espressif/tinyusb/commits/7049c58a0e895acc92c6407574b05b5536eddfc8) identifies the merge “Necessary fixes for TinyUSB 0.21.” No later published upstream release was present. Two legacy-path requests, [upstream](https://raw.githubusercontent.com/hathach/tinyusb/master/docs/info/changelog.rst) and [fork](https://raw.githubusercontent.com/espressif/tinyusb/master/docs/info/changelog.rst), returned 404; [docs listing](https://api.github.com/repos/hathach/tinyusb/contents/docs) then [changelog directory](https://api.github.com/repos/hathach/tinyusb/contents/docs/changelog) resolved the moved layout and confirmed 0.21.0 as its highest listed version. Unreleased commits/PRs were not exhaustively searched.
|
||||
- **mDNS:** [esp-protocols release page 1](https://api.github.com/repos/espressif/esp-protocols/releases?per_page=100&page=1) returned 100 records and [page 2](https://api.github.com/repos/espressif/esp-protocols/releases?per_page=100&page=2) 37. Post-pin mDNS releases in that finite list are **1.13.0 (September 14)** and **1.13.1 (September 15)**. Their notes and both production-source fix patches were inspected; other monorepo components are outside this slice.
|
||||
- **LED:** [Releases API](https://api.github.com/repos/espressif/idf-extra-components/releases?per_page=100&page=1) returned zero. Official [LED changelog](https://raw.githubusercontent.com/espressif/idf-extra-components/master/led_strip/CHANGELOG.md) ends at **3.0.3**, matching installed version.
|
||||
- **xterm:** [Releases API](https://api.github.com/repos/xtermjs/xterm.js/releases?per_page=100&page=1) returned 89 records with no pagination. Read **5.5.0** baseline and **6.0.0 (December 22, 2025)** post-pin release notes. 5.5.0 explicitly lists addon-fit 0.10.0 as compatible. Screened 6.0.0's security-relevant addon fixes and actual integration below; did not mistake npm development/demo dependency updates for packages served by this firmware.
|
||||
|
||||
This completes the **declared finite search**, not every possible vendor/CVE channel. NVD, OSV, npm's global advisory database, private advisories, all historical SDK subdependency advisories, every unreleased commit, and all non-security-labelled release regressions were not exhaustively searched. The failed full-release-database fetches remain visible above.
|
||||
|
||||
## IDF pages 2–3: all twelve previously omitted entries
|
||||
|
||||
All linked advisory bodies below were included in the fetched official API response. “Not-current-path” does not mean the underlying library is patched.
|
||||
|
||||
| Advisory / CVE | Required feature and snapshot disposition |
|
||||
| --- | --- |
|
||||
| [GHSA-9j5x-rf36-54x9](https://github.com/espressif/esp-idf/security/advisories/GHSA-9j5x-rf36-54x9), CVE-2026-25508 | BLE provisioning ATT prepared-write length accumulation. **Not-current-path:** Bluetooth/provisioning absent. |
|
||||
| [GHSA-m2h2-683f-9mw7](https://github.com/espressif/esp-idf/security/advisories/GHSA-m2h2-683f-9mw7), CVE-2026-25532 | WPS enrollee fragment underflow requires initiating WPS PBC/PIN pairing. **Not-current-path:** no application `esp_wifi_wps_*` calls; no enabled WPS setting in the inspected generated header. Ordinary PSK/SAE association is not WPS. |
|
||||
| [GHSA-hmjj-rjvv-w8pq](https://github.com/espressif/esp-idf/security/advisories/GHSA-hmjj-rjvv-w8pq), CVE-2025-68473 | Bluedroid Classic SDP UUID-array overflow. **Not-current-path:** Bluetooth absent. |
|
||||
| [GHSA-43gh-7r4f-qp57](https://github.com/espressif/esp-idf/security/advisories/GHSA-43gh-7r4f-qp57), CVE-2025-68474 | Bluedroid Classic AVRCP vendor-command allocation/write. **Not-current-path:** Bluetooth absent. |
|
||||
| [GHSA-vcw6-jc3p-4gj8](https://github.com/espressif/esp-idf/security/advisories/GHSA-vcw6-jc3p-4gj8), CVE-2025-65092 | ESP32-P4 hardware JPEG header parser. **Not-current-path:** ESP32-S3, no JPEG driver compilation inputs. |
|
||||
| [GHSA-qhf9-vr2h-jh96](https://github.com/espressif/esp-idf/security/advisories/GHSA-qhf9-vr2h-jh96), CVE-2025-66409 | Bluedroid Classic AVRCP vendor-command read. **Not-current-path:** Bluetooth absent. |
|
||||
| [GHSA-8mg7-9qpg-p92v](https://github.com/espressif/esp-idf/security/advisories/GHSA-8mg7-9qpg-p92v), CVE-2025-64342 | Original ESP32 Bluetooth controller invalid access address. **Not-current-path:** S3 explicitly outside affected chip family; Bluetooth also absent. |
|
||||
| [GHSA-9w88-r2vm-qfc4](https://github.com/espressif/esp-idf/security/advisories/GHSA-9w88-r2vm-qfc4), CVE-2025-55297 | BluFi example credential/DH buffers. **Not-current-path:** no BluFi integration, no Bluetooth host. Not a generic flaw in this project's Wi-Fi password handling. |
|
||||
| [GHSA-hqhh-cp47-fv5g](https://github.com/espressif/esp-idf/security/advisories/GHSA-hqhh-cp47-fv5g), CVE-2025-52471 | ESP-NOW receive underflow. **Not-current-path:** no application ESP-NOW initialization or receive callback. Does not prove opaque Wi-Fi archives are globally fixed. |
|
||||
| [GHSA-wm57-466g-mhrr](https://github.com/espressif/esp-idf/security/advisories/GHSA-wm57-466g-mhrr), CVE-2024-53845 | ESPTouch v2 constant AES-CBC IV. **Not-current-path:** no SmartConfig/ESPTouch provisioning calls; PSK/SAE manager is separate. |
|
||||
| [GHSA-22x6-3756-pfp8](https://github.com/espressif/esp-idf/security/advisories/GHSA-22x6-3756-pfp8), CVE-2024-28183 | Physical flash TOCTOU against anti-rollback. **Excluded/not configured:** no enabled `CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK`; physical firmware replacement is outside current review model. No claim of secure boot or rollback protection. |
|
||||
| [GHSA-7f7f-jj2q-28wm](https://github.com/espressif/esp-idf/security/advisories/GHSA-7f7f-jj2q-28wm), CVE-2022-24893 | BLE Mesh provisioning `SegN` bounds. **Not-current-path:** Bluetooth/Mesh absent. |
|
||||
|
||||
Rechecked local evidence: target `esp32s3`/Xtensa; no enabled `CONFIG_BT_ENABLED`; compilation-entry counts `/bt/host/`, `esp_driver_jpeg`, `/esp_tee/` each zero. Targeted application searches for WPS, ESP-NOW, SmartConfig, BluFi and protocomm found no integration. These exclusions do not depend on later release tables implicitly excluding 5.5.0. Revisit before enabling these features.
|
||||
|
||||
Together with the ten entries already dispositioned in [the IDF review](idf_security_review.md#bounded-idf-advisory-index-snapshot-disabledunused-features), this accounts for all **22 unique IDs** in the dated official index.
|
||||
|
||||
## New release-note finding: Wi-Fi management-frame corruption
|
||||
|
||||
**Priority: prompt coherent vendor correction review for PMF-capable SoftAP operation. Not fixed by existing DHCP/mbedTLS overrides.** Reported to the parent during this review.
|
||||
|
||||
Official [IDF 5.5.3 release](https://github.com/espressif/esp-idf/releases/tag/v5.5.3) says “Fixed memory corruption issue by ensuring management frames are only encrypted when they are robust management frames,” referencing **`a3927641`**. Fetched [patch](https://github.com/espressif/esp-idf/commit/a3927641.patch) and [commit API](https://api.github.com/repos/espressif/esp-idf/commits/a3927641): merge commit **`a3927641bfd0aedfaab3514cba3d7fb01378ce4d`**, contained patch commit **`caf4cad3b422be5c2f93afeff4cd07796a262b46`**. The different patch-header ID is the contained commit, not an invented match to the merge hash.
|
||||
|
||||
The patch description states that when the supplicant sends an authentication response for an already-connected station with installed keys, unconditional management-packet encryption based on `bss->pmf_enable` assumed extra encryption-header space that was not present. It also mentions incorrect allocation due to RSN IE length mismatch; that second trigger was not independently established for the project's short configured IE set.
|
||||
|
||||
### Current application prerequisites and original interface
|
||||
|
||||
`src/wifi_manager.c:315–331`, `configure_ap()`:
|
||||
|
||||
```c
|
||||
wifi_config.ap.authmode = WIFI_AUTH_WPA2_WPA3_PSK;
|
||||
wifi_config.ap.max_connection = 4U;
|
||||
wifi_config.ap.pmf_cfg.capable = true;
|
||||
wifi_config.ap.pmf_cfg.required = false;
|
||||
wifi_config.ap.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
|
||||
esp_err_t error = esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
|
||||
```
|
||||
|
||||
`required=false` does **not** disable negotiated PMF. AP/APSTA is a supported runtime policy (`wifi_manager.c:382,646`), not an unused SDK feature; generated `CONFIG_ESP_WIFI_SOFTAP_SUPPORT=1` and WPA3 SAE support are enabled. Exposure is conditional on AP operation and the vendor-described station/key/PMF state. No claim is made that an arbitrary unassociated peer can exploit it.
|
||||
|
||||
Original configured compilation input `components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c:146–155` still contains:
|
||||
|
||||
```c
|
||||
uint8_t *wpa_ap_get_wpa_ie(uint8_t *ie_len)
|
||||
{
|
||||
/* ... existing hostapd checks ... */
|
||||
*ie_len = hapd->wpa_auth->wpa_ie_len;
|
||||
return hapd->wpa_auth->wpa_ie;
|
||||
}
|
||||
```
|
||||
|
||||
`esp_wifi_driver.h:133` has the matching `uint8_t *len` callback signature; `esp_wpa_main.c:489–496` registers it under `CONFIG_ESP_WIFI_SOFTAP_SUPPORT`. This is an interface/provenance marker for the old bundle, **not the source location of the opaque management-encryption defect**.
|
||||
|
||||
### Exact binary-bundle provenance
|
||||
|
||||
The official fix changes **both** callback signatures to `size_t *` and replaces `components/esp_wifi/lib` with **`a4e903fe43bf09a95022f9802db43d39740ccc0b`**. A C-only signature edit is not an adequate or ABI-safe implementation of this correction.
|
||||
|
||||
A local `git rev-parse` in the packaged Wi-Fi submodule failed because its Git metadata is not usable. Instead, calculated Git blob SHA-1 values directly from all seven installed `esp32s3/*.a` files and compared them with official content metadata:
|
||||
|
||||
- [IDF v5.5 submodule pointer](https://api.github.com/repos/espressif/esp-idf/contents/components/esp_wifi/lib?ref=v5.5): **`8a1b7bbc00e895d040c5c9a6fb9d1db2bbfc7958`**.
|
||||
- [Baseline ESP32-S3 archive listing](https://api.github.com/repos/espressif/esp32-wifi-lib/contents/esp32s3?ref=8a1b7bbc00e895d040c5c9a6fb9d1db2bbfc7958): **all seven local blob IDs match** (`core`, `espnow`, `mesh`, `net80211`, `pp`, `smartconfig`, `wapi`).
|
||||
- [Corrected bundle listing](https://api.github.com/repos/espressif/esp32-wifi-lib/contents/esp32s3?ref=a4e903fe43bf09a95022f9802db43d39740ccc0b): all seven differ from installed.
|
||||
- Installed `libnet80211.a`: Git blob `6ee20bec142638dc0a442b9642de9fa62db6ddd0`, SHA-256 **`dcb524dda8563976cf1a97d4398c7b06cf024b7ef9a5f07788e84c88792d8071`**; corrected bundle's Git blob is `8ce8a2a6a9a753e31508bd609bf0a0d1d44c332f`.
|
||||
|
||||
This confirms the installed stock-baseline bundle, not a hidden same-version binary replacement carrying the published fix. It does not independently reverse-engineer the affected routine or establish a reliable remote attack.
|
||||
|
||||
**Parent action:** investigate a maintained IDF/vendor bundle carrying the complete correction, with matching supplicant ABI and all existing source-pinned overrides rebased and revalidated. Do not replace one archive or change only the callback type. Preserve USB/UART0 recovery and broker isolation. Target regression should cover PMF-capable WPA2/WPA3 SoftAP association, repeated authentication/reassociation with keys already installed, APSTA transitions and loaded service recovery. These tests were **not performed**. No blind recommendation to weaken PMF or disable recovery AP policy is made.
|
||||
|
||||
## Additional IDF release candidates screened
|
||||
|
||||
These are supplemental to, not replacements for, the existing detailed IDF review.
|
||||
|
||||
| Release candidate / fetched evidence | Actual-use disposition |
|
||||
| --- | --- |
|
||||
| 5.5.1 DHCP **client** option/HW-ID length, [bcd56c74 patch](https://github.com/espressif/esp-idf/commit/bcd56c74.patch), contained commit `1ad41e589ff7296c484ba64f206f2278a09dab37` | Original `lwip_default_hooks.c:262–269,293–300` retains old checks; generated option length **68**, vendor class ID disabled. Client path is used, but current hostname is fixed `CONFIG_LWIP_LOCAL_HOSTNAME="espressif"` and no application `esp_netif_set_hostname()` call exists. Inspected `dhcp_select()`/`dhcp_discover()` build bounded fixed options, short hostname and six-byte MAC, leaving space in 68 bytes; no current long-option trigger established. mDNS suffix is **not** the DHCP hostname. Revisit/backport before longer hostnames or extra options. Distinct from already fixed DHCP **server** CVE-2026-45160. |
|
||||
| 5.5.2 `esp_timer_dump()` overflow, [a511f3be patch](https://github.com/espressif/esp-idf/commit/a511f3be.patch), contained commit `ec71bd189f6ef42aff2daa99f35d98611bb9faa3` | Original `esp_timer.c:613,616` retains old line-size constants 90/46 (fix 103/47) and profiling branch's incorrect size addition. No application dump call; timer profiling absent. **Not-current application path**, not a globally patched timer library or a ban on ordinary timers. |
|
||||
| 5.5.3 HTTP **client** Digest OOB read, [6d0a7a01 patch](https://github.com/espressif/esp-idf/commit/6d0a7a01.patch), contained commit `bbcc13be8b38283547883bfee4920331bea80cd1` | Original `esp_http_client/lib/http_auth.c:131–132` uses fixed-length `memcmp` on server algorithm string; no application HTTP-client/Digest consumer. **Not-current-path**; the HTTPS server's cookie login is not this function. |
|
||||
| 5.5.3 AES DMA/PSRAM cache ordering, [3f10cdab patch](https://github.com/espressif/esp-idf/commit/3f10cdab.patch), contained commits `525ef3a2eab239e30cd4cc62f6d23b61743fe4c1`, `0742f3fce3a1779c63ec286342f6d9799e12822f` | Original `esp_aes_dma_core.c:1121–1128` invalidates after DMA; file is a configured compilation input. S3 has `SOC_PSRAM_DMA_CAPABLE=1`, so **target name alone does not exclude it**. Generated `CONFIG_MBEDTLS_HARDWARE_AES` absent; `esp_config.h:150–153` therefore undefines `MBEDTLS_AES_ALT`, and no application `esp_aes_*` call was found. No current application mbedTLS hardware-AES path established. Direct SDK/opaque-radio consumers and their destination buffers were not exhaustively traced: retain this as a **residual SDK applicability question**, not global exclusion or a verified current HTTPS corruption finding. |
|
||||
| Bluetooth, provisioning, JPEG, TREL/OpenThread, other-chip secure-boot/flash-encryption release fixes | Bluetooth/JPEG/provisioning exclusions above apply; project is not an OpenThread/TREL application. Physical security and other-chip configuration changes do not establish an S3 network attack. These release categories were screened, not their entire implementations audited. |
|
||||
|
||||
## Managed USB applicability
|
||||
|
||||
Published esp-usb records are [GHSA-gp8r-qjfr-gqfv](https://github.com/espressif/esp-usb/security/advisories/GHSA-gp8r-qjfr-gqfv) (HID host close double-free race), [GHSA-2pm2-62mr-c9x7](https://github.com/espressif/esp-usb/security/advisories/GHSA-2pm2-62mr-c9x7) (HID host descriptor UAF), and [GHSA-g65h-9ggq-9827](https://github.com/espressif/esp-usb/security/advisories/GHSA-g65h-9ggq-9827) (UVC host descriptor-printing stack overflow). Official affected components are `usb_host_hid` through 1.0.4 and `usb_host_uvc` through 2.3.1, **not similarly numbered esp_tinyusb releases**.
|
||||
|
||||
`src/usb_cdc_transport.c:763–781` calls `TINYUSB_DEFAULT_CONFIG(device_event_callback)`, `tinyusb_driver_install(&usb_config)`, and `tinyusb_cdcacm_init(&cdc_config)` for `TINYUSB_CDC_ACM_0`, using the S3 internal full-speed PHY. Generated settings enable one CDC, 1,024-byte RX/TX buffers, 512-byte CDC endpoint buffer, no HID/MIDI/vendor instances, no DFU/network class. `esp_tinyusb/include/tusb_config.h:88` enables device mode and maps class counts at lines 192–201. Filtered compilation database contains **zero** HID-host, UVC-host, or TinyUSB `src/host` entries. Thus all three published host advisories are **not-current-path**, without relying on “USB requires physical access” as an exclusion.
|
||||
|
||||
Post-pin wrapper changelog 2.3.0 describes MTP, IDF6 MSC compatibility and power-management/light-sleep integration, not a newly identified CDC memory-safety fix. MSC/MTP is not used; `CONFIG_PM_ENABLE` absent. Upstream TinyUSB's newest published 0.21.0 notes include EP0 OUT copy clamping, HID descriptor bounds and MSC callback-size caps. These are baseline-release fixes rather than evidence that a later release must be backported. Spot-check: installed `src/device/usbd.c:881,920` limits `data_len` to `wLength` and clamps transferred bytes to remaining control data. SHA-256 **`38de54351d81878e6543f2b022d0c39f45d28c8f0888491796398dec45e727be`**. This is not a complete USB control-request audit or proof every upstream fix is in the fork.
|
||||
|
||||
## mDNS post-pin fixes
|
||||
|
||||
mDNS is network-reachable when STA discovery starts: `src/wifi_manager.c:117` calls the application service, whose `src/mdns_service.c:154–160` calls `mdns_init()`, `mdns_hostname_set()` and `mdns_instance_name_set()`. It is **not globally disabled**. The generated profile enables only predefined STA, maximum one interface/service, and has no `CONFIG_MDNS_ENABLE_BROWSE`. No application `mdns_service_add*`, `mdns_query_*` or `mdns_browse_*` calls were found.
|
||||
|
||||
1. **1.13.0 duplicate DNS-SD question expansion:** fetched [c56c725dd5e77c83c1a0683fce6dd3521e05cea2](https://github.com/espressif/esp-protocols/commit/c56c725d.patch). Upstream test explicitly calls repeated `_services._dns-sd._udp.local` PTR questions “heap amplification”: each expands the registered service list. Installed `mdns_receive.c:711–716` lacks the new `if (parsed_packet->discovery) continue;` guard and allocates inside `while (a)` after `a = mdns_priv_get_services()`. **Primitive unpatched, no current amplification trigger:** no services registered; `mdns_responder.c:38–45` zeroes the server and `:102–104` returns its initially empty service list. A hostname/instance label is not a service registration, and `MAX_SERVICES=1` is a capacity, not one active service. Reassess before advertising `_https`, `_ssh`, or any DNS-SD service; the guard is a sensible narrow defense-in-depth candidate, not a demonstrated current responder DoS fix.
|
||||
2. **1.13.1 staged-IP NUL termination:** fetched [b370a9abc29390b4adc612d251cc2dbbb325ff6f](https://github.com/espressif/esp-protocols/commit/b370a9ab.patch). Adds `new_staged_ip->hostname[MDNS_NAME_BUF_LEN - 1] = '\0';`; commit describes a GCC truncation warning. The installed receiver does **not** contain `rx_staged_ip_add()`; browsing is also disabled. Do not assert an installed unterminated-buffer vulnerability simply from version ordering or the release-note title. No current-path defect established.
|
||||
|
||||
Installed receiver SHA-256: **`9ada93eb2f89080a28bf2a849871e054032f06889c40397555de527af0e3d7bd`**. Earlier mDNS release notes mention parser/browse hardening, but this slice did not re-audit every pre-pin fix or equate the absence of a GHSA with a safe parser.
|
||||
|
||||
## LED strip and offline browser assets
|
||||
|
||||
**LED:** `src/status_led.c:15–36` fixes `max_leds=1`, WS2812/GRB, RMT, `with_dma=false`; `:72–77` sets pixel index zero then refreshes. No peer-controlled strip allocation or index is supplied. No later release/security correction was identified in the declared official channels; no blanket driver safety claim.
|
||||
|
||||
**Browser integration:** `src/web_ui.c:1507–1519` creates a terminal with `allowProposedApi:false`, loads only `FitAddon`, and `:1552–1554` does the same for admin. `:1531` feeds received bytes to `target.write(new Uint8Array(data), ...)`. Serial-device output is not automatically trustworthy just because a browser session authenticated; escape-sequence rendering remains a real boundary.
|
||||
|
||||
Post-pin 6.0.0 candidate dispositions:
|
||||
|
||||
- [PR5020 patch](https://github.com/xtermjs/xterm.js/pull/5020.patch), “Escape Unsafe HTML Characters in addon-serialize,” changes **addon-serialize HTML output**, not the core terminal renderer. That addon and `serializeAsHTML()` integration are absent. **Not-current-path**, not an assertion that all xterm 5.5 HTML rendering was repaired.
|
||||
- [PR5355 patch](https://github.com/xtermjs/xterm.js/pull/5355.patch), “Add note about reverse tabnapping,” is **documentation-only in addon-web-links typings**, warning about iframe/custom-handler behavior. The addon is not loaded. Do not report this as a core security fix missing from 5.5.0.
|
||||
- Core OSC 8 links exist even without addon-web-links. Checked [5.5.0 upstream provider](https://raw.githubusercontent.com/xtermjs/xterm.js/5.5.0/src/browser/OscLinkProvider.ts) and targeted matching snippets in hash-verified local JS: absent `allowNonHttpProtocols`, protocols are limited to `http:`/`https:`; default activation confirms navigation and sets `newWindow.opener=null` before assigning location. Application supplies no custom `linkHandler`. This rebuts an automatic arbitrary-protocol/tabnapping finding from these release notes, not all phishing/social-engineering risks or browser implementation issues.
|
||||
- Other release-note changes include OSC parsing/correctness, readonly behavior for disabled input, selection/renderer fixes, and new clipboard/progress functionality. Only fit is installed; search, image, serialize, web-links, ligatures, clipboard and WebGL addons are not supplied. Build/demo updates to Express, ws, webpack, axios, etc. are not automatically firmware/browser runtime dependencies. No additional current security trigger was established from that release-note screen.
|
||||
|
||||
A future xterm upgrade would need deliberate addon compatibility, offline asset provenance/regeneration and browser regression review; no asset or version change is authorized by this document.
|
||||
|
||||
## Validation and handoff limits
|
||||
|
||||
Performed: official bounded advisory/release fetches; all 22 IDF index-entry accounting; targeted source/config/caller and configured-input inspection; managed manifest checks; two local web-asset hash checks; seven Wi-Fi archive Git-blob comparisons; source snippet/hash capture. Final checks passed for document whitespace/final newline, six local links, dated scope, and twelve additional IDF plus three USB advisory IDs. Document-scoped `git diff --check` also passed (the new untracked file was explicitly checked by Python). Final Git status showed concurrent changes in release-notice/validation/icon-provenance files outside this ownership scope; none were edited or reverted here. No firmware build, host regression execution, browser test, USB transaction, radio packet test, fault injection, upload, monitor, erase, or target resource measurement was performed.
|
||||
|
||||
**Parent summary:** carry forward the Wi-Fi bundle correction investigation and the residual direct-SDK AES-DMA applicability question. Do not describe either as implemented, or the AES question as an established current-path vulnerability. Preserve prior detailed wolf/IDF review authority and all existing mitigations. The omitted official IDF index pages and named managed/browser publication channels now have a finite dated coverage record; full release-database contents, unpublished issues, opaque implementation analysis and hardware acceptance remain outside the completed evidence. Re-run a bounded publication check before release or when dependencies, enabled features, callers, or trust boundaries change.
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
**Missing generated modification/date notices: resolved; current count is eight C sources plus one header.** `tools/security_overrides.py` prepends the baseline project modification notice dated **2026-09-15**, identifying the editable generator and warning against editing derived copies. The wolfSSH `internal.c`, added `ssh.c` and generated `wolfssh/internal.h` also receive a prominent **2026-09-16** ordering-profile/provenance notice. Exact original hashes and exact-once edits remain required; upstream notices are retained. The header is a PUBLIC forced build input, not a ninth C source. Both new mbedTLS copies retain their original Mbed TLS Contributors copyright and **`Apache-2.0 OR GPL-2.0-or-later`** SPDX header; the Apache-2.0 selection in this inventory is unchanged. This resolves the narrow notice-generation finding, **not release packaging or license clearance**.
|
||||
|
||||
Offline notice assembly tooling is implemented; actual firmware/device/browser delivery, radio-blob corresponding-source/exception questions, preferred-source packaging, wolfSSH license-package discrepancy, exact icon provenance and release-specific runtime/bootloader review remain open. [Offline notice assembly](release_packaging.md) is implemented by [tools/release_notices.py](../tools/release_notices.py): 62 mandatory hash/size-pinned inputs, deterministic bounded outputs, fail-closed preflight and no overwrite/fetch/build/device access. Parent notices suite: 30 PASS. Supplied independent review found no actionable scoped parser/bundle defects and verified two actual 62-input bundles were deterministic. The previously measured actual bundle was 64 files / 541,147 bytes; that is snapshot evidence, not a newly measured bundle size or legal clearance. Include `cmake/wolf_crypto_policy.{cmake,h}` and the ordering inputs described below with the reproducible build/source package. No upgrades, asset regeneration or device operations were performed for this update.
|
||||
Offline notice assembly tooling is implemented; actual firmware/device/browser delivery, radio-blob corresponding-source/exception questions, preferred-source packaging, wolfSSH license-package discrepancy, manual bitmap/mockup provenance limits and release-specific runtime/bootloader review remain open; [exact retained SVG provenance](icon_provenance.md) is resolved. [Offline notice assembly](release_packaging.md) is implemented by [tools/release_notices.py](../tools/release_notices.py): 75 mandatory hash/size-pinned inputs, deterministic bounded outputs, fail-closed preflight and no overwrite/fetch/build/device access. Supplied agent evidence: **36 notice fixture tests PASS; two actual bundles verified deterministic, each 77 files / 4,433,930 bytes**. This includes retained SVGs and [exact icon provenance](icon_provenance.md), not corresponding-source delivery or legal clearance. These are agent measurements, not a parent rebuild or reassembly in this documentation turn. Include `cmake/wolf_crypto_policy.{cmake,h}` and the ordering inputs described below with the reproducible build/source package. No upgrades, asset regeneration or device operations were performed for this update.
|
||||
|
||||
## Historical inventory baseline — 2026-09-15
|
||||
|
||||
@@ -82,10 +82,10 @@ The installed license-path inventory also found OpenThread, NimBLE/Bluetooth, mi
|
||||
| --- | --- | --- |
|
||||
| `@xterm/xterm` | **5.5.0**; `web_assets/xterm.js`, `xterm.css`, `xterm.LICENSE`; [SOURCES.md](../web_assets/SOURCES.md) provides exact npm URLs/hashes | `MIT`. Full notice retained in repository; CSS also retains a notice. Minified JS has no `copyright`, `permission is hereby`, or `license` marker. |
|
||||
| `@xterm/addon-fit` | **0.10.0**; `web_assets/addon-fit.js`, `addon-fit.LICENSE`; same provenance document | `MIT`. Full notice retained in repository; minified JS lacks those notice markers. |
|
||||
| Pictogrammers Material Design Icons | **7.4.47 according to `src/local_status_ui.c`**; `third_party/material-design-icons/usb.svg`, `wifi-strength-4.svg`, `LICENSE` | Source comments identify hand-rasterized USB and adapted Wi-Fi masks as Apache-2.0 icon derivatives. Retained license is **“Pictogrammers Free License” summary**, referring icons to Apache-2.0 or their respective licenses, code to MIT. **Not the full Apache-2.0 text or a per-icon origin record.** Version/individual-icon licensing is not independently authenticated by a local package manifest. |
|
||||
| Pictogrammers Material Design Icons | Official **7.4.47**, immutable commit `9e04201d4557e729822fb57f62a316c3dea1d4a8`; [exact evidence](icon_provenance.md) | Retained SVGs are byte-identical to upstream; metadata credits **Google (USB)** and **Simran (Wi-Fi)**. Apache-2.0 package evidence, full license text and a pinned-tree check finding no NOTICE are retained. Manual firmware bitmap derivation and the distinct Wi-Fi mockup path remain unresolved; source identity is not complete derivative or release clearance. |
|
||||
| Project logo | `web_assets/logo.png`, derived from `images/logo.png` per SOURCES.md | Project-owned according to provenance document; no independent third-party license assigned. Generator's MIT banner must not be treated as proof that the logo was separately relicensed MIT. |
|
||||
|
||||
`git ls-files '*LICENSE*' '*COPYING*' '*NOTICE*'` found **4 tracked license files**: root GPLv3, two web MIT texts, and the icon summary. Existing `third_party/` licensing is therefore **not sufficient as a complete release notice bundle**. It also does not supply the missing full Apache text for the icon derivatives.
|
||||
Historical 2026-09-15 `git ls-files '*LICENSE*' '*COPYING*' '*NOTICE*'` found **4 tracked license files**: root GPLv3, two web MIT texts, and the icon summary. That historical count predates the retained upstream icon evidence and full Apache text now included in the 75-input catalog. Neither repository retention nor assembly proves recipient delivery or release compliance.
|
||||
|
||||
`web_assets/generate_embedded_assets.py` embeds three gzip files and the logo, **not either MIT license file**. Its generated-source banner points back to provenance/licenses; it is not the upstream MIT notice itself. This proves the embedding path does not carry the separate notice texts, not that every possible product-delivery channel lacks notices. Coordinate a release notice bundle and a way for recipients of the browser assets to obtain the notices (for example a bundled/served third-party license resource); verify the actual delivery path. Do not assume repository-only notices accompany a standalone firmware/device or a downloaded JS response. No assets or web endpoints were changed in this audit.
|
||||
|
||||
@@ -139,8 +139,8 @@ Notice assembly is implemented; the remaining release actions below have not bee
|
||||
| Priority | Action / completion evidence |
|
||||
| --- | --- |
|
||||
| Release blocker to resolve | Document a defensible corresponding-source/exception basis for the **selected radio blobs** and other source-unavailable linked content. This is an unresolved question, not a legal verdict. |
|
||||
| Assembly tooling implemented; delivery pending | [Offline tool and procedure](release_packaging.md) assemble 62 pinned inputs, retaining GPL/Apache/MIT/BSD texts, mixed SDK/toolchain Newlib and argtable3 notices, GCC exception and a scoped full TLSF rendering. Parent fixture suite: 30 PASS; two actual bundles independently verified deterministic. Validate final firmware/device/browser recipient delivery; assembly is not corresponding-source delivery or legal clearance. |
|
||||
| Before distributing icon derivatives | Full Apache-2.0 text is supplied in the scoped bundle via SDK LICENSE; still confirm exact two-icon provenance/licensing at 7.4.47; retain upstream attribution and any applicable NOTICE. Existing source comments already identify adaptations. Do not invent an absent upstream NOTICE or copy unrelated icon licenses. |
|
||||
| Assembly tooling implemented; delivery pending | [Offline tool and procedure](release_packaging.md) assemble 75 pinned inputs, retaining GPL/Apache/MIT/BSD texts, mixed SDK/toolchain Newlib and argtable3 notices, GCC exception and a scoped full TLSF rendering. Supplied agent fixture suite: 36 PASS; two actual bundles independently verified deterministic, each 77 files / 4,433,930 bytes. Validate final firmware/device/browser recipient delivery; assembly is not corresponding-source delivery or legal clearance. |
|
||||
| Before distributing icon derivatives | [Exact retained SVG provenance](icon_provenance.md) is resolved at official 7.4.47; full Apache-2.0 text and attribution are retained, with no NOTICE found in the pinned distribution. Preserve manual bitmap preferred sources and resolve the distinct mockup Wi-Fi path/derivation limits. Recipient delivery remains open; do not invent a NOTICE or rasterizer. |
|
||||
| Resolved in generator; retain in distribution | Prominent modification notices cover **eight C sources plus one header** (baseline **2026-09-15**, wolfSSH ordering **2026-09-16**), with original notices retained. Release packaging must still include them, archived patches/provenance, consolidated delta and reproducible build inputs. |
|
||||
| Upstream clarification | Record/resolve wolfSSH 1.4.20 package LICENSE/README vs source-header mismatch without editing the managed package in place. GPLv3 is supported by the inspected headers and is also within the package prose's “or later” wording. |
|
||||
| Release packaging verification | Archive exact corresponding source/build inputs and required asset preferred sources; validate source delivery/offer and any User Product installation information. Recheck bootloader, runtime objects and aggregation boundaries against the final release map. |
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# Exact USB and Wi-Fi icon provenance
|
||||
|
||||
Reviewed 2026-09-16. This record resolves the identity of the **two retained
|
||||
preferred-form SVG sources at Material Design Icons 7.4.47**, not all project
|
||||
artwork, historical authorship of manual edits, or release compliance.
|
||||
No firmware, mockup, web asset, or generated asset was changed or regenerated.
|
||||
|
||||
## Official pinned source and licenses
|
||||
|
||||
Official SVG distribution: <https://github.com/Templarian/MaterialDesign-SVG>.
|
||||
Its upstream README identifies this as the production SVG distribution and
|
||||
points to the main MaterialDesign repository for issues. The main repository
|
||||
URL with tag `v7.4.47` returned 404; it is not used as version evidence.
|
||||
|
||||
- Distribution tag: `v7.4.47`.
|
||||
- Annotated tag object: `5edde266e281d26a03dcfa89fb651183cbab0f2e`.
|
||||
- Peeled commit: `9e04201d4557e729822fb57f62a316c3dea1d4a8`.
|
||||
- Upstream `package.json`: `@mdi/svg`, version `7.4.47`, license `Apache-2.0`.
|
||||
- Tag timestamp: `2023-12-26T23:33:26Z`; GitHub reports it **unsigned**.
|
||||
HTTPS retrieval and content pins are evidence, not signature authentication.
|
||||
|
||||
Unmodified upstream SVGs, LICENSE, README, package metadata, complete `meta.json`,
|
||||
and GitHub tag/tree responses are retained in
|
||||
[`third_party/material-design-icons/upstream-7.4.47/`](../third_party/material-design-icons/upstream-7.4.47/).
|
||||
`downloads.json` records exact retrieval URLs, sizes and SHA-256 hashes. Raw
|
||||
source URLs use the immutable commit, not a moving branch or tag. API responses
|
||||
are retained as fetched; future API formatting is not assumed stable.
|
||||
The full metadata/tree files intentionally preserve the evidence behind the
|
||||
per-icon selections and absence check, rather than only project-written claims.
|
||||
|
||||
The existing `third_party/material-design-icons/LICENSE` is byte-identical to
|
||||
the pinned upstream Pictogrammers Free License summary and remains unchanged.
|
||||
It distinguishes icon Apache-2.0/respective grants from code MIT terms. The
|
||||
package declares Apache-2.0; the two metadata entries contain authors but no
|
||||
per-icon alternate grant. This is the scoped upstream licensing evidence for
|
||||
these two SVGs, not a blanket conclusion about every icon in the collection.
|
||||
`upstream-7.4.47/Apache-2.0.txt` supplies the **full Apache-2.0 text**, fetched
|
||||
from <https://www.apache.org/licenses/LICENSE-2.0.txt> (SHA-256
|
||||
`cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30`).
|
||||
It is a separately sourced license text, not a file falsely attributed to the
|
||||
upstream icon tree. The retained non-truncated recursive tree has no path
|
||||
containing `notice` (case-insensitive); no upstream NOTICE was found in this
|
||||
pinned distribution and none is fabricated here. This does not audit unrelated
|
||||
repositories or assert that a NOTICE is mandatory for every Apache work.
|
||||
|
||||
| Icon | Metadata attribution | Stable icon ID | First-version metadata | SVG SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `usb` | Google | `25033E0B-3AD4-414D-9972-559F2690FC1D` | `1.5.54` | `c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22` |
|
||||
| `wifi-strength-4` | Simran | `41B86B22-7245-4A97-9BAA-3E9EBD44CEB0` | `2.3.50` | `89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f` |
|
||||
|
||||
The metadata `version` fields describe icon introduction versions, **not** a
|
||||
contradiction of package 7.4.47. Matching these sources to that release does not
|
||||
prove they originated in that release or were originally downloaded from it.
|
||||
|
||||
## Exact comparison to project derivatives
|
||||
|
||||
1. **Retained SVGs:** `third_party/material-design-icons/usb.svg` (403 bytes)
|
||||
and `wifi-strength-4.svg` (219 bytes) are each byte-for-byte identical to the
|
||||
pinned upstream originals, including every path command, coordinate, arc
|
||||
flag and viewBox. Each has one path, `viewBox="0 0 24 24"`, and no transform
|
||||
on the root or path. No coordinate rounding, normalization or visual-match
|
||||
inference was used. The files remain unchanged.
|
||||
2. **USB mockup:** `docs/phase7c_icon_mockup.svg` symbol `usb` has exactly the
|
||||
upstream `d` string and 24-by-24 viewBox, without symbol/path transforms.
|
||||
Its two direct use definitions are `(x,y,width,height)=(150,4,30,30)` inside
|
||||
reusable `status`, and `(8,72,30,30)` on the broker page. These apply uniform
|
||||
scale `30/24 = 1.25` and the stated translations. Status is instantiated in
|
||||
page groups translated by `(62,98)`, `(592,98)`, `(62,438)`, `(592,438)`;
|
||||
the broker use is in `(62,438)`. Thus status USB geometry maps as
|
||||
`(X,Y)=(pageX+150+1.25*x, pageY+4+1.25*y)` and broker USB as
|
||||
`(X,Y)=(62+8+1.25*x,438+72+1.25*y)`. CSS adds presentation fill/stroke;
|
||||
this is path geometry equivalence, not pixel/rendering equivalence.
|
||||
3. **Wi-Fi mockup:** symbol `wifi` in that same mockup uses a different,
|
||||
segmented path starting `M1 8q11-9 22 0`. It is **not** an exact copy of
|
||||
`wifi-strength-4` (which starts `M12,3C7.79,3`). No exact affine derivation
|
||||
or upstream provenance for that distinct mockup path is established here.
|
||||
4. **Actual firmware:** `src/local_status_ui.c` explicitly calls the USB mask
|
||||
hand-rasterized and the Wi-Fi masks compact segmented adaptations. The exact
|
||||
editable row bytes are:
|
||||
|
||||
```text
|
||||
s_icon_usb: 18 3c 18 5a 3e 18 3c 18
|
||||
s_icon_wifi_full: 7e 81 3c 42 18 24 00 18
|
||||
s_icon_wifi_three: 00 00 3c 42 18 24 00 18
|
||||
s_icon_wifi_two: 00 00 00 00 18 24 00 18
|
||||
s_icon_wifi_one: 00 00 00 00 00 00 00 18
|
||||
```
|
||||
|
||||
These manual 8-by-8 masks are not SVG coordinates or exact scaled vector
|
||||
paths. No deterministic rasterizer, sampling/threshold rule or edit history
|
||||
establishes an exact upstream-to-mask transform. Their declared design
|
||||
provenance is supported by the now-verified retained sources, but **exact
|
||||
mechanical derivation of the firmware masks remains unresolved**. Preserve
|
||||
the C arrays as the preferred editable form of those manual modifications,
|
||||
together with both original SVGs; do not substitute an invented generator.
|
||||
The existing source comment is the adaptation notice. This dated record
|
||||
documents the inspected modifications; it does not invent their edit date.
|
||||
|
||||
## Offline checks and release boundary
|
||||
|
||||
`python3 tests/release_notices/run.py` verifies the pinned source bytes and Git
|
||||
blob identities, version/author metadata, catalog inclusion, exact SVG geometry,
|
||||
USB mockup placement/ancestry, distinct Wi-Fi mockup path, and current manual
|
||||
mask bytes. Negative checks reject coordinate and transform changes. Tests do
|
||||
not regenerate artwork or claim raster equivalence.
|
||||
|
||||
The notice catalog retains all previous inputs and adds the two existing SVGs,
|
||||
these source originals, licensing/metadata evidence and this record. The bundle
|
||||
is still **not corresponding source or release clearance**. Firmware C arrays
|
||||
and the full mockup remain in the project, not copied into this bounded notice
|
||||
bundle; include them in a separately reviewed corresponding-source delivery
|
||||
where required. Radio-blob/source-exception questions, wolfSSH packaging,
|
||||
recipient notice/source delivery and other release gates remain open.
|
||||
|
||||
Older global inventory/packaging/memory documents describe the pre-verification
|
||||
state and counts. They were intentionally not edited in this scoped task;
|
||||
this record and the current catalog provide the narrower updated evidence.
|
||||
@@ -0,0 +1,172 @@
|
||||
# Phase 9 bounded host validation
|
||||
|
||||
`tools/validate_phase9.py` is a thin, sequential orchestrator for existing test
|
||||
runners, not a new security test implementation or a Phase 9 acceptance gate.
|
||||
See [security hardening](security_hardening.md) for scope, historical evidence,
|
||||
and the deferred whole-phase target checklist.
|
||||
|
||||
## Usage
|
||||
|
||||
From the repository root (the script itself also resolves the root independently
|
||||
of the caller's working directory):
|
||||
|
||||
```sh
|
||||
python3 -B tools/validate_phase9.py --dry-run
|
||||
python3 -B tools/validate_phase9.py
|
||||
python3 -B tools/validate_phase9.py --fail-fast
|
||||
python3 -B tools/validate_phase9.py --timeout 300
|
||||
```
|
||||
|
||||
Build and OpenSSH interoperability are **independent explicit opt-ins**:
|
||||
|
||||
```sh
|
||||
python3 -B tools/validate_phase9.py --build --build-timeout 600
|
||||
python3 -B tools/validate_phase9.py --interop --timeout 300
|
||||
python3 -B tools/validate_phase9.py --build --interop --dry-run
|
||||
```
|
||||
|
||||
These examples are usage, not execution claims. `--build` runs exactly `pio run`
|
||||
first; it does not upload, erase, monitor, or select another PlatformIO environment.
|
||||
PlatformIO itself may download dependencies or write its normal external caches.
|
||||
The caller/parent must choose whether to build and arrange any required permission
|
||||
separately. No installer, permission grant, network retry, or sandbox escalation
|
||||
is implemented. A failed/timed-out build skips every dependent host command to
|
||||
avoid testing stale artifacts as if that build succeeded.
|
||||
|
||||
`--interop` adds only `--interop` to `tests/wolfssh_order_contract/run.py` (its
|
||||
default single matrix). That runner uses OpenSSH and local AF_UNIX sockets, not
|
||||
an IP host or device. Socket permissions and installed OpenSSH tooling must
|
||||
already be available; denial is a failure, not a reason to grant permissions.
|
||||
Neither option is enabled by default. No target address, credentials, arbitrary
|
||||
command, or device-operation argument is accepted.
|
||||
|
||||
## Coverage and prerequisites
|
||||
|
||||
The fixed default plan contains **23 commands**; `--dry-run` prints their exact
|
||||
argv vectors and timeouts without launching anything or checking prerequisites.
|
||||
There is no discovery-time execution of runners (some run at import/top level).
|
||||
Paths and options were inspected in their actual `run.py` implementations.
|
||||
|
||||
| Scope | Existing runners / modes |
|
||||
|---|---|
|
||||
| 9A effective crash policy | `security_build_policy --sdkconfig-header .pio/build/esp32-s3-devkitc-1-n16r8/config/sdkconfig.h` |
|
||||
| 9B admission / credentials | `ssh_auth_policy`, `ssh_auth_transport`, `hidden_input` |
|
||||
| 9C allocator | `ssh_memory` |
|
||||
| Current seven-suite baseline | `sdk_security_overrides --build-dir .pio/build/esp32-s3-devkitc-1-n16r8`, `wolfssh_auth_contract`, `ssh_protocol_policy`, `wolf_crypto_policy`, `wolfssh_parser_contract`, `wolfssh_order_contract`, `release_notices` |
|
||||
| Focused SSH/admin and HTTPS lifecycle regressions | `ssh_management`, `admin_console_boundary`, `admin_ssh_policy`, `web_admin_transport`, `web_admin_tickets`, `web_httpd_idle` |
|
||||
| Web early admission, credentials and administrative lifecycle | `web_cookie_auth` default plus separate `--admin`, `--accounts`, `--ssh`, `--lifecycle` commands |
|
||||
|
||||
Every runner path is `tests/<name>/run.py`. Auth contract, protocol policy, and
|
||||
crypto policy receive explicit `--compile-commands
|
||||
.pio/build/esp32-s3-devkitc-1-n16r8/compile_commands.json` arguments. No
|
||||
`--host-only`, candidate injection, or silent fallback is used. Default operation
|
||||
requires an existing configured build, installed pinned SDK/managed sources,
|
||||
the production toolchain and host compiler/libraries needed by these runners
|
||||
(including OpenSSL development support for relevant tests). It **does not run
|
||||
PlatformIO** to satisfy those requirements. Missing explicit files/executables
|
||||
are `PREREQ` failures; deeper source/toolchain prerequisites are checked by the
|
||||
existing runners and surface as nonzero `FAIL` results. Existing artifacts are
|
||||
not proof that the current checkout has been freshly built.
|
||||
|
||||
The current cookie-auth runner has no `--admission` branch: the default exercises
|
||||
early admission/wiping, and each selected domain mode repeats that baseline.
|
||||
Unrelated display/network/UI/performance suites are intentionally excluded.
|
||||
`ssh_memory` retains its documented optional installed-IDF extent-source check
|
||||
as skipped (no `--idf-path` supplied); its normal allocator contracts still run.
|
||||
The ordering runner's candidate `--target-contracts`, separate `--pio-adapter`
|
||||
regression, and repeated interop matrices are not added implicitly. Run those
|
||||
separately when their particular evidence is needed. This is a focused full
|
||||
Phase 9 host regression set, not every test in the repository.
|
||||
|
||||
## Bounds, output, and result semantics
|
||||
|
||||
- Each host command has a 180-second wall-clock timeout by default; the optional
|
||||
build gets 600 seconds. Both timeout flags accept finite values in `(0, 3600]`.
|
||||
The fixed sequential plan bounds total child execution by the sum of its
|
||||
timeouts (default 4,140 seconds), plus launch/reporting/cleanup overhead.
|
||||
- POSIX process groups are required. Timeout or Ctrl-C kills the active process
|
||||
group and reaps the runner; successful/failed exit also retires any remaining
|
||||
group descendants. This covers normal compiler/test children, not deliberately
|
||||
detached processes. Group reaping has a five-second wait bound.
|
||||
- Commands use argv lists, no shell evaluation or interpolation, with stdin
|
||||
closed. The child environment is copied intact except `CCACHE_DISABLE=1`;
|
||||
arbitrary compiler, SDK, proxy and other caller environment settings are not
|
||||
overwritten. The parent's environment is not mutated.
|
||||
- stdout/stderr are inherited live. The orchestrator creates no persistent log,
|
||||
JSON capture, environment dump, or secret artifact. Existing runners retain
|
||||
their own temporary fixture/build behavior; forced termination can bypass
|
||||
their temporary-directory cleanup. Console output is **not a redaction
|
||||
guarantee**: use synthetic fixtures, review before sharing, and do not redirect
|
||||
output into routine secret-bearing persistent captures. Build outputs remain
|
||||
normal PlatformIO artifacts only when explicitly requested.
|
||||
- `PASS` means the entire runner exited zero, not that an earlier printed PASS
|
||||
line appeared. The final summary records each command's exit, timeout or
|
||||
missing prerequisite. Defaults collect failures and continue independent
|
||||
commands; `--fail-fast` reports remaining commands as `SKIP`. Build failure
|
||||
always stops dependent commands. No selected suite is silently dropped.
|
||||
- Exit 0 means all selected commands passed, or a dry-run plan was printed.
|
||||
Dry-run uses `PLAN`, never `PASS`, and is not validation. Failures, timeouts,
|
||||
prerequisites and dependent skips return 1; invalid CLI arguments return 2;
|
||||
Ctrl-C returns 130. Optional build/interop omissions are announced separately
|
||||
and do not turn a successful host run into failure.
|
||||
|
||||
## Orchestrator fixture tests
|
||||
|
||||
```sh
|
||||
python3 -B tests/phase9_validation/run.py
|
||||
```
|
||||
|
||||
Ten offline unittest groups check the exact plan/paths/strict arguments, independent
|
||||
opt-ins, timeout configuration and invalid values, dry-run nonexecution, default
|
||||
build/interop omission, failure collection, fail-fast and failed-build skips,
|
||||
missing file/executable handling, literal argv/environment preservation,
|
||||
timeout/descendant termination, and inherited streams/closed stdin. They execute
|
||||
only small Python fixtures, never PlatformIO, interop, devices, or network.
|
||||
|
||||
## Execution record — 2026-09-16
|
||||
|
||||
Initial orchestrator-agent execution, under the existing sandbox without elevation (historical first run):
|
||||
|
||||
- `python3 -B tests/phase9_validation/run.py`: **10 tests PASS**.
|
||||
- `python3 -B tools/validate_phase9.py --dry-run`: exit 0; **plan only**, no
|
||||
prerequisite or suite validation.
|
||||
- `python3 -B tools/validate_phase9.py`: **exit 1, 22 PASS / 1 FAIL**, no timeouts;
|
||||
about 57 seconds of summed command time. Build and interop explicitly skipped.
|
||||
- Direct rerun `CCACHE_DISABLE=1 python3 -B tests/admin_console_boundary/run.py`:
|
||||
reproduced exit 1 at line 40, `ValueError: substring not found` for
|
||||
`ssh.index("static void *ssh_malloc(")`. Earlier subtest PASS lines do not make
|
||||
the runner pass. The existing runner is outside this change's ownership and
|
||||
was not edited.
|
||||
|
||||
Passed commands: `security_build_policy`, `ssh_auth_policy`, `ssh_auth_transport`,
|
||||
`hidden_input`, `ssh_memory`, **all seven current baseline suites listed above**,
|
||||
`ssh_management`, `admin_ssh_policy`, `web_admin_transport`, `web_admin_tickets`,
|
||||
`web_httpd_idle`, and all five selected `web_cookie_auth` modes.
|
||||
Only `admin_console_boundary` failed.
|
||||
|
||||
No firmware build, interop, device, runtime reserve, hardware panic/recovery,
|
||||
release delivery, or whole-Phase-9 acceptance result is claimed for this task.
|
||||
|
||||
### Follow-up supplied evidence
|
||||
|
||||
The owning agent corrected the stale `admin_console_boundary` source anchor;
|
||||
its subsequent default orchestrator run reports **23/23 PASS**. This supersedes
|
||||
the initial 22/23 host result above, without turning its early PASS lines into
|
||||
a successful initial run. Final supplied parent execution now confirms:
|
||||
|
||||
- `CCACHE_DISABLE=1 python3 -B tools/validate_phase9.py`: **PASS 23/23**.
|
||||
- `tests/phase9_validation/run.py`: **PASS 10 tests**.
|
||||
|
||||
No build or interop ran this turn; both remain separate opt-ins, not default
|
||||
execution. Firmware source is unchanged. These are actual parent results supplied
|
||||
for this record, not another execution by this documentation integration.
|
||||
|
||||
The supplied notice agent also reports **36 fixture tests PASS** and two actual
|
||||
75-input bundles, each **77 files / 4,433,930 bytes**, verified deterministic; see
|
||||
[packaging evidence](release_packaging.md). These are agent measurements, not a
|
||||
new build or bundle measurement by this documentation integration.
|
||||
|
||||
The [finite advisory search](dependency_advisory_coverage.md) is complete for its
|
||||
declared snapshot, but the [PMF-capable SoftAP blocker](wifi_security_update_plan.md)
|
||||
is **not fixed**. Host success does not repair that vendor bundle, validate radio
|
||||
behavior, clear distribution obligations or complete Phase 9.
|
||||
@@ -40,8 +40,10 @@ traversal, binary notice payloads and exhausted bounds fail closed. Symlinks in
|
||||
input-root or output-parent ancestry are rejected too: pass real non-symlinked
|
||||
paths, not a convenience symlink. All input preflight completes before creating
|
||||
output. The limits are 128 inputs, 4 MiB per input/catalog and 32 MiB cumulative
|
||||
source bytes. The current catalog has 62 inputs (about 2.1 MB read and 0.51 MB
|
||||
copied before the generated manifest/introduction).
|
||||
source bytes. The current catalog has **75 inputs**, including the retained SVGs and pinned
|
||||
[icon provenance evidence](icon_provenance.md). Overview documents edited in this
|
||||
integration are not catalog inputs; `docs/icon_provenance.md` **is pinned**.
|
||||
Coordinate any change to that record with the catalog owner; never silently repin.
|
||||
|
||||
An existing output file, directory (even empty) or dangling symlink is never
|
||||
overwritten. Parent directories are not automatically created. Output uses
|
||||
@@ -58,7 +60,8 @@ checks prevent symlink traversal but are not isolation from the same OS user.
|
||||
- `README.txt`: limitations and entry point for recipients.
|
||||
- `inputs/project/…`: project GPL text, resolved lock, managed licenses and
|
||||
manifests, wolfSSL/wolfSSH README evidence, original wolfSSH header excerpts,
|
||||
xterm MIT texts and recorded provenance, icon summary, TLSF full rendering
|
||||
xterm MIT texts and recorded provenance, icon summary and official 7.4.47
|
||||
SVG/license/metadata evidence, TLSF full rendering
|
||||
and scope/provenance statement.
|
||||
- `inputs/sdk/…`: complete scoped SDK license documents, mixed Newlib/argtable3
|
||||
notices, radio license files, package/version identity and original source
|
||||
@@ -122,15 +125,27 @@ catalog policy and test it. The runtime does not discover added notices or
|
||||
packages outside its finite list; this limitation makes its reads bounded and
|
||||
prevents opportunistic collection of user files.
|
||||
|
||||
## Supplied execution evidence — 2026-09-16
|
||||
|
||||
Agent results: **36 notice fixture tests PASS**, and **two actual 75-input
|
||||
bundles verified deterministic**, each **77 files / 4,433,930 bytes**. These
|
||||
replace the earlier 62-input / 64-file / 541,147-byte snapshot, not its historical
|
||||
record. No parent rebuild or bundle reassembly was run in this documentation
|
||||
turn; firmware source is unchanged. Assembly is not legal clearance or Phase 9
|
||||
acceptance. The [unfixed Wi-Fi blocker](wifi_security_update_plan.md) also remains
|
||||
a separate security gate; a future coherent vendor update requires catalog review.
|
||||
|
||||
## Separate work before distribution
|
||||
|
||||
1. Resolve or obtain qualified review of radio-blob corresponding-source or
|
||||
exception basis for the actual linked firmware. Apache binary permissions
|
||||
alone do not settle this question. No legal clearance is claimed.
|
||||
2. Resolve the wolfSSH package/header discrepancy and verify exact two-icon
|
||||
provenance/licensing (recorded 7.4.47), including any applicable NOTICE.
|
||||
Supplying the full Apache text is not provenance verification. No icon
|
||||
assets or generated data were changed by this packaging work.
|
||||
2. Resolve the wolfSSH package/header discrepancy and remaining icon derivative
|
||||
questions. [Exact retained SVG provenance](icon_provenance.md) is verified at
|
||||
official 7.4.47, commit `9e04201d4557e729822fb57f62a316c3dea1d4a8`: Google
|
||||
USB and Simran Wi-Fi. Full Apache text is retained; no NOTICE was found in
|
||||
that pinned distribution. Manual firmware bitmap derivation and the distinct
|
||||
mockup Wi-Fi path remain unresolved. No artwork was regenerated.
|
||||
3. Audit the final application/bootloader/runtime object set and aggregation
|
||||
boundaries. Full SDK/toolchain redistribution needs broader nested/tool/test
|
||||
notice coverage; this is not an all-files license scanner.
|
||||
|
||||
+5
-3
@@ -216,13 +216,15 @@ Staged work:
|
||||
1. **9A — Crash/debug build policy and operational profiles — In progress; hardware pending.** `src/security_build_policy.c` requires `CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y` and `CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT=y`; rejects core-dump enable/flash/UART, panic print/halt/GDBstub, runtime GDBstub and ESP/FreeRTOS debugger-aware options. `sdkconfig.defaults` makes the baseline explicit. Development/test/production use the same build baseline, not separate PlatformIO environments. Host matrix (`python3 tests/security_build_policy/run.py`) compiles the actual guard: 17 cases plus the generated-header check passed on 2026-09-15. `pio run` passed (94,196 B linked RAM / 1,828,565 B flash); target panic/recovery tests have not run. Production readiness remains pending.
|
||||
2. **9B — SSH admission and credential handling — Implemented; combined target validation deferred.** Boot-lifetime, owner-only token buckets independently bound handshakes, password/signed-key requests and unsigned probes; reconnect/restart/counter clearing do not replenish them. Existing per-slot attempt limits/currentness remain. Explicit keyboard-interactive rejection, pending-signature result fencing, secret-free admission counters, consumed admin-buffer wipes and fail-closed hidden-prompt overflow/unsupported-byte handling are implemented. Four focused suites (including 35 pinned-vendor control-flow cases), 11 related regressions and `pio run` passed on 2026-09-15: 94,340 B linked RAM / 1,829,925 B flash. Global-budget starvation remains a documented tradeoff, not a solved availability problem.
|
||||
3. **9C — Library cleanup and protocol policy — Implemented; combined target validation deferred.** Exact-hash build-tree overrides correct HTTPS cleanup/leaks, HTTPD scratch failure/wiping/first-read handling, bounded SSH password parsing/wiping and server-local TLS policy without modifying installed dependencies. Secure wolfSSL allocation hooks and explicit SSH policy fail closed; early web quota probing avoids receiving already-throttled bodies. TLS1.2 ECDHE-ECDSA AES-GCM and SSH GCM/modern-KEX allowlists intentionally exclude legacy-only clients; no identity migration. Bounded password/certificate/header/destructor review is documented, not exhaustive zeroization. Final build PASS 94,340 B linked RAM / 1,831,309 B flash; focused and related host/source-contract tests passed. [Review and maintenance contract](security_library_review.md).
|
||||
4. **9D — Maintenance and lifecycle — In progress; unresolved security and distribution questions.** [Security operations](security_operations.md) documents provisioning, explicit saves, rotation, recovery, reconstruction (no implemented backup/export) and approved destructive reset/decommissioning. The [wolfSSL/wolfSSH implementation addendum](wolf_security_review.md) records the implemented Xtensa small X25519/Ed25519 mitigation (CVE-2025-12888), with consistent library/consumer flags and a resolved-settings guard; small X25519 is not combined with blinding. Bounded IGNORE/service/string parsing, channel-window overflow, ECC/Ed25519 labels and exact signature framing are now corrected in the existing generated override. [P-256 import and X25519 all-zero-result checks](ssh_key_validation_review.md) are enabled with effective PUBLIC flags confirmed; the [finite remaining SSH parser/caller review](ssh_parser_remaining_review.md) is complete, not exhaustive safety. Exact CHANNEL_FAILURE recipient, `ssh-userauth` service and all nine channel-name checks are implemented; unknown/trailing-payload behavior is retained. PR899 client skips remain unchanged behind server ordering, forwarding is disabled, and the inspected generic caller trace has no short-digest/OID trigger; generic APIs are not globally fixed. **Restricted existing-profile ordering correction (CVE-2025-14942) is implemented**, using audited PR793/819/840/855/921 subsets plus local gates, not a full upstream backport. X25519/P-256 only, independent SELF/PEER bits, expected-message/authentication gates and queued NEWKEYS continuation handle nonblocking writes. **EXT_INFO is intentionally disabled; no `server-sig-algs` is advertised.** Eight C overrides plus one PUBLIC forced ABI header retain hash pins/notices; joined forced-header arguments fix PlatformIO sorting/deduplication. The review's misplaced EOF guard is corrected and verified with no scoped blocker. The [IDF implementation addendum](idf_security_review.md) records pinned DHCP (CVE-2026-45160), TLS 1.2 EMS (CVE-2026-50581) and X.509 allocation-failure (CVE-2026-34874) backports; historical research remains labeled and retained. The [finite IDF applicability completion](idf_security_review.md#finite-applicability-completion--2026-09-16) dispositions all six named findings: ECDH small-output, zero-length ECC PK parse, basicConstraints, server NewSessionTicket and stale ASN.1 length are not current paths/configurations for their documented reasons; optimized ECC reduction is active but its privileged-local/physical side-channel attacker model is excluded. No new current-path correction was established. Only the first advisory-index page was screened; unpatched primitives and broader coverage remain, not “all CVEs safe.”
|
||||
4. **9D — Maintenance and lifecycle — In progress; unresolved security and distribution questions.** [Security operations](security_operations.md) documents provisioning, explicit saves, rotation, recovery, reconstruction (no implemented backup/export) and approved destructive reset/decommissioning. The [wolfSSL/wolfSSH implementation addendum](wolf_security_review.md) records the implemented Xtensa small X25519/Ed25519 mitigation (CVE-2025-12888), with consistent library/consumer flags and a resolved-settings guard; small X25519 is not combined with blinding. Bounded IGNORE/service/string parsing, channel-window overflow, ECC/Ed25519 labels and exact signature framing are now corrected in the existing generated override. [P-256 import and X25519 all-zero-result checks](ssh_key_validation_review.md) are enabled with effective PUBLIC flags confirmed; the [finite remaining SSH parser/caller review](ssh_parser_remaining_review.md) is complete, not exhaustive safety. Exact CHANNEL_FAILURE recipient, `ssh-userauth` service and all nine channel-name checks are implemented; unknown/trailing-payload behavior is retained. PR899 client skips remain unchanged behind server ordering, forwarding is disabled, and the inspected generic caller trace has no short-digest/OID trigger; generic APIs are not globally fixed. **Restricted existing-profile ordering correction (CVE-2025-14942) is implemented**, using audited PR793/819/840/855/921 subsets plus local gates, not a full upstream backport. X25519/P-256 only, independent SELF/PEER bits, expected-message/authentication gates and queued NEWKEYS continuation handle nonblocking writes. **EXT_INFO is intentionally disabled; no `server-sig-algs` is advertised.** Eight C overrides plus one PUBLIC forced ABI header retain hash pins/notices; joined forced-header arguments fix PlatformIO sorting/deduplication. The review's misplaced EOF guard is corrected and verified with no scoped blocker. The [IDF implementation addendum](idf_security_review.md) records pinned DHCP (CVE-2026-45160), TLS 1.2 EMS (CVE-2026-50581) and X.509 allocation-failure (CVE-2026-34874) backports; historical research remains labeled and retained. The [finite IDF applicability completion](idf_security_review.md#finite-applicability-completion--2026-09-16) dispositions all six named findings: ECDH small-output, zero-length ECC PK parse, basicConstraints, server NewSessionTicket and stale ASN.1 length are not current paths/configurations for their documented reasons; optimized ECC reduction is active but its privileged-local/physical side-channel attacker model is excluded. No new current-path correction was established. That earlier review screened only the first advisory-index page. The subsequent [finite dependency coverage](dependency_advisory_coverage.md) accounts for all **22 IDF entries across three pages**, plus named managed-component and browser publication channels. The declared dated search is complete, not exhaustive advisory clearance; unpatched primitives, inaccessible full-release databases and fresh findings remain.
|
||||
|
||||
**Latest integration evidence:** supplied parent build PASS: **94,340 B linked RAM / 1,768,901 B flash**, unchanged RAM / **+200 B flash** versus 1,768,701 B. Final parent **all seven suites PASS**: ordering `--interop` (8,028 checks, seven rejected mutations, 12 exact-256-KiB sessions with clean channel close/transport EOF), SDK overrides with actual `--build-dir` registration, auth (135), protocol, strict crypto, notices (30), and parser (3,258 × two modes plus channel profiles; 11 + 18 + 2 rejected mutations). Interop required unsandboxed approval only for local AF_UNIX sockets; no remote network/device operation. Agent ordering evidence: **8,028 checks / seven rejected mutations**. Initial interop failed a harness close race despite early PASS output; the corrected harness waits for peer close/transport EOF and independently reaps the server through an fd-passing proxy. Final agent repeated OpenSSH matrix: **36/36 sessions**, each exact **256 KiB**, **two or ten** completed key exchanges and clean client/server exits; see [evidence and limits](security_hardening.md#ordering-hostbuild-evidence--2026-09-16).
|
||||
|
||||
**Release status:** the [bounded license inventory](dependency_licenses.md) marks modification/date notices resolved for all eight generated C files plus one header (baseline 2026-09-15 and wolfSSH ordering 2026-09-16; upstream licenses retained), with archived patch hashes/commits and patch license context recorded, but radio-blob corresponding-source/exception, source/notice delivery and provenance questions remain open. [Offline notice assembly](release_packaging.md) is implemented by [tools/release_notices.py](../tools/release_notices.py): 62 mandatory hash/size-pinned inputs, deterministic bounded outputs, fail-closed preflight and no overwrite/fetch/build/device access. Parent notices suite: 30 PASS. Supplied independent review found no actionable scoped parser/bundle defects and verified two actual 62-input bundles were deterministic. The previously measured actual bundle was 64 files / 541,147 bytes; that is snapshot evidence, not a newly measured bundle size or legal clearance.
|
||||
**Release status:** the [bounded license inventory](dependency_licenses.md) marks modification/date notices resolved for all eight generated C files plus one header (baseline 2026-09-15 and wolfSSH ordering 2026-09-16; upstream licenses retained), with archived patch hashes/commits and patch license context recorded, but radio-blob corresponding-source/exception, source/notice delivery and provenance questions remain open. [Offline notice assembly](release_packaging.md) is implemented by [tools/release_notices.py](../tools/release_notices.py): 75 mandatory hash/size-pinned inputs, deterministic bounded outputs, fail-closed preflight and no overwrite/fetch/build/device access. Supplied agent evidence: **36 notice fixture tests PASS; two actual bundles verified deterministic, each 77 files / 4,433,930 bytes**. This includes retained SVGs and [exact icon provenance](icon_provenance.md), not corresponding-source delivery or legal clearance. These are agent measurements, not a parent rebuild or reassembly in this documentation turn.
|
||||
|
||||
**Remaining maintenance/target gates:** official registry wolfSSH 1.5.0 / wolfSSL 5.9.2 queries returned 404 on 2026-09-15; upstream tags exist but managed compatibility is not established. The [remaining ordering gates and immutable provenance](wolf_security_review.md#restricted-ordering-correction-and-remaining-gates) retain target cleanup during rekey, intended-client compatibility without EXT_INFO and whole-phase resource/timing validation. Future upstream adoption still requires isolated packaging/compatibility evaluation. Upgrades/backports require coherent source/header review, override rebasing and contract tests, not blind repinning. Broader dependency advisory coverage remains unfinished. No dependency upgrade was performed. **Phase 9 is not complete or production-ready; scoped mitigations do not establish full advisory closure, device validation or license/distribution clearance.** OTA signing trust remains separate Phase 10 work.
|
||||
**Remaining maintenance/target gates:** official registry wolfSSH 1.5.0 / wolfSSL 5.9.2 queries returned 404 on 2026-09-15; upstream tags exist but managed compatibility is not established. The [remaining ordering gates and immutable provenance](wolf_security_review.md#restricted-ordering-correction-and-remaining-gates) retain target cleanup during rekey, intended-client compatibility without EXT_INFO and whole-phase resource/timing validation. Future upstream adoption still requires isolated packaging/compatibility evaluation. Upgrades/backports require coherent source/header review, override rebasing and contract tests, not blind repinning. The [declared finite dependency search](dependency_advisory_coverage.md) is complete; fresh findings and explicit coverage gaps remain. **Current PMF-capable SoftAP security blocker: not fixed.** Follow the [Wi-Fi security update plan](wifi_security_update_plan.md): select a coherent vendor framework/toolchain and source/header/binary ABI bundle, rebase exact security overrides and validate on target; do not transplant one archive, edit only the callback signature or weaken PMF/WPA3. No dependency upgrade was performed. **Phase 9 is not complete or production-ready; scoped mitigations do not establish full advisory closure, device validation or license/distribution clearance.** OTA signing trust remains separate Phase 10 work.
|
||||
|
||||
**Bounded host validation:** [tools/validate_phase9.py](../tools/validate_phase9.py) orchestrates **23 commands** by default, with no build or OpenSSH interop unless separately opted in. The stale console-boundary anchor was fixed by its owning agent; supplied follow-up agent result is **23/23 PASS**. Final supplied parent execution: `CCACHE_DISABLE=1 python3 -B tools/validate_phase9.py` **PASS 23/23**; `tests/phase9_validation/run.py` **PASS 10 tests**. No build or interop ran this turn. See [usage and execution boundaries](phase9_validation.md). No parent rebuild this turn; firmware source is unchanged. Historical build/interop results above are not fresh target or release evidence.
|
||||
|
||||
At the user's request, hardware validation is deferred to **Phase 9 as a whole**, not required between implementation slices. [Security hardening](security_hardening.md) collects profiles, host evidence and the combined target checklist. Silent panic reboot removes useful crash diagnostics, not ordinary reset/boot/status information or every possible log disclosure. Raw flash/RAM/dumps remain secret-bearing, not routine diagnostic exports. Existing coredump bytes are not retroactively cleared; no secure erase is claimed. Isolated synthetic-secret debug builds require explicit reviewed source-policy changes, not a provided bypass flag.
|
||||
|
||||
|
||||
@@ -106,8 +106,8 @@ The existing global five-verifications/60-second fixed window is unchanged: malf
|
||||
|
||||
- [Security operations](security_operations.md) supplies source-checked provisioning, explicit-save, account/key/identity rotation, recovery, reconstruction and approved erase/decommissioning procedures. There is no implemented configuration backup/export or private-identity restore workflow. Commands and checklists are not device execution evidence; the user will validate Phase 9 as a whole.
|
||||
- [wolfSSL/wolfSSH review and implementation addendum](wolf_security_review.md): the **CVE-2025-12888** Xtensa mitigation now selects `CURVE25519_SMALL` and `ED25519_SMALL` consistently for wolfSSL and consumers. The PUBLIC forced-include resolved-settings guard rejects missing small implementations, X25519 blinding (incompatible with small in this pinned version), and unreviewed Curve448/Ed448 enablement. The existing generated wolfSSH override now bounds IGNORE/service strings, rejects zero-capacity string output and channel-window overflow, corrects ECC/Ed25519 key/signature labels, and enforces exact signature framing (ECC nested r/s bounds plus inner/outer consumption; Ed25519 outer consumption). These are PR892/881/880 subsets plus local framing corrections, not full backports; password wiping/async retention remain unchanged. PUBLIC `WOLFSSL_VALIDATE_ECC_IMPORT` and `WOLFSSL_ECDHX_SHARED_NOT_ZERO` now enable P-256 import validation and X25519 all-zero-result rejection, with effective production flags confirmed and fail-closed backend guards. See [key-validation evidence](ssh_key_validation_review.md) and [parser scope/limits](../tests/wolfssh_parser_contract/README.md). The restricted existing-profile correction for **CVE-2025-14942** is implemented as described below. The [finite remaining SSH review](ssh_parser_remaining_review.md) is complete: bounded exact CHANNEL_FAILURE recipient parsing (fatal policy retained), exact `ssh-userauth` service validation, and exact length/byte dispatch for all nine channel-request names are implemented. Unknown-request/trailing-payload behavior is preserved. PR899 client key skips are unchanged and blocked by current server role/ordering; PR918/919 forwarding is disabled. The inspected generic signature caller trace found no attacker-selected short-digest/OID path; generic APIs remain unpatched. These are profile-specific dispositions, not exhaustive parser/library safety; revisit on caller, feature, KEX or source changes. No exploit or whole-library clearance is demonstrated; added validation CPU/allocation cost and target interoperability remain unmeasured.
|
||||
- [Focused IDF review and implementation addendum](idf_security_review.md): pinned backports now implement DHCP option bounds (**CVE-2026-45160**), TLS 1.2 EMS failure return (**CVE-2026-50581**) and X.509 OID allocation-failure handling (**CVE-2026-34874**). Explicit nested-target validation places the mbedTLS edits on `mbedtls`/`mbedx509`, retaining source properties and exactly-one-source checks. WS negotiation **CVE-2026-45541** and ASN.1 named-data **CVE-2025-48965** remain unpatched with the review's qualified applicability, not blanket closure. The [finite IDF applicability completion](idf_security_review.md#finite-applicability-completion--2026-09-16) dispositions all six named findings: ECDH small-output, zero-length ECC PK parse, basicConstraints, server NewSessionTicket and stale ASN.1 length are not current paths/configurations for their documented reasons; optimized ECC reduction is active but its privileged-local/physical side-channel attacker model is excluded. No new current-path correction was established. Only the first advisory-index page was screened; unpatched primitives and broader coverage remain, not “all CVEs safe.”
|
||||
- [Dependency license inventory](dependency_licenses.md) is a bounded engineering inventory, not legal or distribution clearance. All **eight generated C sources plus one header** carry prominent modification notices: the baseline **2026-09-15** notice plus **2026-09-16** ordering/provenance notices on wolfSSH outputs, with upstream notices retained, including both mbedTLS dual-license headers. That narrow finding is resolved. Radio-blob corresponding-source/exception questions, actual firmware/device/browser notice delivery, preferred-source packaging, wolfSSH package-license discrepancy and exact icon provenance and recipient license delivery remain open. Notice assembly does not prove recipient delivery. [Offline notice assembly](release_packaging.md) is implemented by [tools/release_notices.py](../tools/release_notices.py): 62 mandatory hash/size-pinned inputs, deterministic bounded outputs, fail-closed preflight and no overwrite/fetch/build/device access. Parent notices suite: 30 PASS. Supplied independent review found no actionable scoped parser/bundle defects and verified two actual 62-input bundles were deterministic. The previously measured actual bundle was 64 files / 541,147 bytes; that is snapshot evidence, not a newly measured bundle size or legal clearance.
|
||||
- [Focused IDF review and implementation addendum](idf_security_review.md): pinned backports now implement DHCP option bounds (**CVE-2026-45160**), TLS 1.2 EMS failure return (**CVE-2026-50581**) and X.509 OID allocation-failure handling (**CVE-2026-34874**). Explicit nested-target validation places the mbedTLS edits on `mbedtls`/`mbedx509`, retaining source properties and exactly-one-source checks. WS negotiation **CVE-2026-45541** and ASN.1 named-data **CVE-2025-48965** remain unpatched with the review's qualified applicability, not blanket closure. The [finite IDF applicability completion](idf_security_review.md#finite-applicability-completion--2026-09-16) dispositions all six named findings: ECDH small-output, zero-length ECC PK parse, basicConstraints, server NewSessionTicket and stale ASN.1 length are not current paths/configurations for their documented reasons; optimized ECC reduction is active but its privileged-local/physical side-channel attacker model is excluded. No new current-path correction was established. That earlier review screened only the first advisory-index page. The subsequent [finite dependency coverage](dependency_advisory_coverage.md) accounts for all **22 IDF entries across three pages**, plus named managed-component and browser publication channels. The declared dated search is complete, not exhaustive advisory clearance; unpatched primitives, inaccessible full-release databases and fresh findings remain.
|
||||
- [Dependency license inventory](dependency_licenses.md) is a bounded engineering inventory, not legal or distribution clearance. All **eight generated C sources plus one header** carry prominent modification notices: the baseline **2026-09-15** notice plus **2026-09-16** ordering/provenance notices on wolfSSH outputs, with upstream notices retained, including both mbedTLS dual-license headers. That narrow finding is resolved. Radio-blob corresponding-source/exception questions, actual firmware/device/browser notice delivery, preferred-source packaging, wolfSSH package-license discrepancy and manual bitmap derivation, the distinct mockup Wi-Fi path and recipient license delivery remain open; [exact retained SVG provenance](icon_provenance.md) is resolved. Notice assembly does not prove recipient delivery. [Offline notice assembly](release_packaging.md) is implemented by [tools/release_notices.py](../tools/release_notices.py): 75 mandatory hash/size-pinned inputs, deterministic bounded outputs, fail-closed preflight and no overwrite/fetch/build/device access. Supplied agent evidence: **36 notice fixture tests PASS; two actual bundles verified deterministic, each 77 files / 4,433,930 bytes**. This includes retained SVGs and [exact icon provenance](icon_provenance.md), not corresponding-source delivery or legal clearance. These are agent measurements, not a parent rebuild or reassembly in this documentation turn.
|
||||
- **Restricted ordering correction implemented, not a full upstream backport or sign-off.** The [ordering review](wolf_security_review.md#restricted-ordering-correction-and-remaining-gates) and [provenance/prerequisite disposition](../tools/wolfssh_order/README.md) document the audited PR793/819/840/855/921 subsets plus local gates. Existing X25519/P-256 KEX only; independent SELF/PEER state, exact expected replies and authentication-phase checks cover both roles. Queued NEWKEYS survives WANT_WRITE without duplication. **EXT_INFO is deliberately disabled; no `server-sig-algs` is sent**, and `extInfoSent` stays zero. CMake applies the generated ABI header BEFORE PUBLIC and via a PUBLIC forced include; joined `-include/path` flags fix PlatformIO sorting/deduplication for ordering and crypto guards. Review's misplaced EOF guard is corrected before channel mutation; verification found no scoped blocker. Target cleanup during rekey and no-EXT_INFO client compatibility remain pending.
|
||||
- No dependency versions were upgraded. Beyond the implemented backports above, proposed upgrades/backports remain candidates, not approved compatible versions. Re-audit coherent source/header changes, effective compile policy, exact-hash overrides and callback/parser contracts, then obtain host/build and whole-phase target evidence. 9A–9C passes below are historical scoped evidence, not closure of these newly recorded findings.
|
||||
|
||||
@@ -206,13 +206,15 @@ Retain these checks for the user's final whole-phase test session; do not stop i
|
||||
4. Repeatedly start/stop HTTPS and SSH and stress header parsing/authentication under the full transport mix. Capture internal/DMA/PSRAM free/minimum/largest-block and stack margins alongside serial/broker loss counters. Specifically measure old-plus-new allocation peaks and secure-free CPU cost; previous very low internal minima remain important.
|
||||
5. Verify exhausted web login returns early without stalled-body work, clears the used pre-login challenge, and recovers after the documented window. Check malformed requests below quota and correct credentials for normal behavior; do not infer fairness from a rate-limit pass.
|
||||
|
||||
**Bounded host validation:** [tools/validate_phase9.py](../tools/validate_phase9.py) orchestrates **23 commands** by default, with no build or OpenSSH interop unless separately opted in. The stale console-boundary anchor was fixed by its owning agent; supplied follow-up agent result is **23/23 PASS**. Final supplied parent execution: `CCACHE_DISABLE=1 python3 -B tools/validate_phase9.py` **PASS 23/23**; `tests/phase9_validation/run.py` **PASS 10 tests**. No build or interop ran this turn. See [usage and execution boundaries](phase9_validation.md). No parent rebuild this turn; firmware source is unchanged. Historical build/interop results above are not fresh target or release evidence.
|
||||
|
||||
## Staged next work
|
||||
|
||||
- **Implementation/maintenance gate:** the finite SSH and six-finding IDF reviews are complete for their stated profiles; do not re-list them as unimplemented. Finish broader advisory coverage and resolve any newly established current-path findings with pinned changes and fresh production-source tests. Optional ASN.1/ECDH/basicConstraints defense-in-depth backports are not implemented or required by a demonstrated current-path finding.
|
||||
- **Release gate:** notice assembly is implemented; validate actual firmware/device/browser delivery, corresponding source and preferred asset sources, radio-blob legal basis, wolfSSH packaging clarification, exact icon provenance, final runtime/bootloader attribution and any Installation Information. See [packaging gates](release_packaging.md#separate-work-before-distribution).
|
||||
- **Implementation/maintenance gate:** the finite SSH and six-finding IDF reviews are complete for their stated profiles; do not re-list them as unimplemented. The [declared finite advisory search](dependency_advisory_coverage.md) is complete. Resolve the **unfixed PMF-capable SoftAP security blocker** through the [coherent vendor framework/ABI update plan](wifi_security_update_plan.md), retaining PMF/WPA3, exact override guards and fresh production-source/target tests. Residual direct-SDK AES-DMA applicability and publication-channel gaps remain; recheck the bounded snapshot before release. Optional ASN.1/ECDH/basicConstraints defense-in-depth backports are not implemented or required by a demonstrated current-path finding.
|
||||
- **Release gate:** notice assembly is implemented; validate actual firmware/device/browser delivery, corresponding source and preferred asset sources, radio-blob legal basis, wolfSSH packaging clarification, remaining manual bitmap/mockup provenance, final runtime/bootloader attribution and any Installation Information. See [packaging gates](release_packaging.md#separate-work-before-distribution).
|
||||
- **Target/acceptance gate:** rehearse operations and the combined target checklist above, including panic/recovery, abuse/isolation, cleanup during rekey, intended-client compatibility without EXT_INFO, loaded KEX/rekey and heap/stack/CPU timing. Obtain explicit whole-phase acceptance; no scoped reviewer or host PASS can substitute.
|
||||
|
||||
- **Continue 9D maintenance and lifecycle.** Execute the [remaining ordering gates and advisory work](wolf_security_review.md#restricted-ordering-correction-and-remaining-gates), finish broader dependency coverage beyond the bounded IDF/mbedTLS review and three implemented backports, and address the [release source/notice work](dependency_licenses.md#actionable-release-work-not-performed). Runbooks are documented, not rehearsed; remaining mitigations/reviews, distribution clearance and whole-phase acceptance remain outstanding.
|
||||
- **Continue 9D maintenance and lifecycle.** Execute the [remaining ordering gates and advisory work](wolf_security_review.md#restricted-ordering-correction-and-remaining-gates), implement and validate the coherent Wi-Fi correction identified by the completed finite dependency search, and revisit its explicit residual questions, and address the [release source/notice work](dependency_licenses.md#actionable-release-work-not-performed). Runbooks are documented, not rehearsed; remaining mitigations/reviews, distribution clearance and whole-phase acceptance remain outstanding.
|
||||
- **Retained evidence limits:** 9C completes a bounded cleanup/protocol review, not every-library-copy zeroization. Live inline residue, compaction tails, hardware/stack intermediates, global admission starvation and resource/interop measurements remain documented limitations or combined target gates. Any additional hardening must preserve owner lifetimes and bounded recovery.
|
||||
- **Phase 10: OTA trust.** Define independent image-signature verification, trust-anchor provisioning, rotation/revocation, rollback/downgrade and recovery policy without secure boot. Authenticated transport alone is not image-signing policy, and OTA signature checks cannot prevent physical firmware replacement.
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# Wi-Fi management-frame security update plan
|
||||
|
||||
Review date: **2026-09-16**. Focused documentation-only follow-up to [dependency advisory coverage](dependency_advisory_coverage.md#new-release-note-finding-wi-fi-management-frame-corruption). **No correction implemented, exploit reproduced, build run, device accessed, or upgrade approved.** This document is the only file owned/created by this review; existing concurrent work is untouched.
|
||||
|
||||
## Decision
|
||||
|
||||
**Prefer a complete, immutable Espressif IDF 5.5 maintenance-release bundle, integrated through a compatible pinned PlatformIO package/toolchain, over transplanting Wi-Fi archives into IDF 5.5.0.** IDF **5.5.3 is the verified fix-bearing reference candidate/minimum demonstrated release here**, not a recommendation to deploy it in preference to all later maintenance releases. The earlier coverage records 5.5.4/5.5.5; selecting a newer deployment candidate requires the same immutable provenance and compatibility gates below, not a repeat broad advisory audit.
|
||||
|
||||
Do not change PMF/WPA3 policy as an automatic mitigation. Do not replace only `libnet80211.a`, edit only a callback declaration, relax source guards, or overwrite the installed SDK. Current PMF-capable SoftAP exposure remains open pending implementation and target validation.
|
||||
|
||||
## Current applicability and trigger limits
|
||||
|
||||
Rechecked authoritative source `src/wifi_manager.c:315–363`:
|
||||
|
||||
- SoftAP uses `WIFI_AUTH_WPA2_WPA3_PSK`, four clients, `pmf_cfg.capable=true`, `required=false`, `WPA3_SAE_PWE_BOTH`. AP/APSTA are real supported paths, including live fallback transitions (`set_runtime_ap_enabled`). Optional PMF is **not PMF disabled**.
|
||||
- Station uses PMF capability in both profiles; mixed has WPA2 minimum/optional PMF, WPA3 requires PMF. Do not downgrade station profiles to address a demonstrated SoftAP trigger.
|
||||
- Existing generated `sdkconfig.h` identifies ESP32-S3 and enables SoftAP, WPA3 SAE, SoftAP SAE, SAE H2E and SAE-PK support. This is build metadata, not proof of a running device's current mode or negotiated association.
|
||||
- Architecture: the permanent Wi-Fi manager owns driver operations and bounded events; application NVS is authoritative, driver storage is RAM-only, AP policy is off/fallback/always. Working edits require explicit save. Preserve manager ownership, secret wiping and secret-free status. UART0 recovery and native USB UART1 access must remain independent of network success. Broker single-writer/multiple-isolated-observer behavior is unchanged.
|
||||
|
||||
The official contained fix says that when the supplicant sends an **authentication response for an already-connected station, with keys installed after a successful four-way handshake**, management encryption was selected from `bss->pmf_enable` without checking whether the frame was robust. The encryption code assumed header space that had not been allocated, causing memory corruption. The fix checks robust-management-frame status before encryption.
|
||||
|
||||
This supports a current-feature exposure candidate when SoftAP is active with the relevant station/key/PMF state. Radio delivery of the relevant management exchange, rather than application HTTPS/SSH authentication, is the pertinent access path (inference from the described Wi-Fi operation). The public text does **not** establish whether an attacker must know the PSK, personally complete association, or can spoof an existing station's exchange. It supplies no packet-level proof, CVSS, reliable arbitrary-code-execution claim, or exhaustive affected-mode matrix. Do not characterize this as proven unauthenticated remote exploitation, nor assume application login protects it. STA-only exclusion is limited to the described AP-response path, not a blanket clean bill for the opaque bundle.
|
||||
|
||||
The same vendor change also fixes an RSN-IE length/allocation mismatch, deauthentication-reason parsing, and default SAE PWE values. The project's short configured IE set has not been shown to trigger the allocation issue; do not conflate that uncertainty with the established old ABI/bundle identity.
|
||||
|
||||
## Official immutable provenance
|
||||
|
||||
Official sources fetched in this review:
|
||||
|
||||
1. [5.5.3 release](https://github.com/espressif/esp-idf/releases/tag/v5.5.3), also [release API](https://api.github.com/repos/espressif/esp-idf/releases/tags/v5.5.3): explicitly names the management-frame memory-corruption correction under `a3927641`.
|
||||
2. [Merge commit](https://github.com/espressif/esp-idf/commit/a3927641bfd0aedfaab3514cba3d7fb01378ce4d), checked through its official commit API: **`a3927641bfd0aedfaab3514cba3d7fb01378ce4d`**. Its title emphasizes SAE defaults; the title alone omits the security detail.
|
||||
3. [Contained fix patch](https://github.com/espressif/esp-idf/commit/caf4cad3b422be5c2f93afeff4cd07796a262b46.patch): **`caf4cad3b422be5c2f93afeff4cd07796a262b46`**, “Backport some wifi fixes,” with the trigger explanation. This is the merge's second parent, not a conflicting identification.
|
||||
4. [Annotated release tag object](https://api.github.com/repos/espressif/esp-idf/git/tags/b31fcc7a314a44ad992b58f589f7d1d8a4fadff6): tag object **`b31fcc7a314a44ad992b58f589f7d1d8a4fadff6`** resolves to IDF commit **`2c211b236707889e8400c4dc5644dd5c4ee071e0`**. [Immutable comparison](https://api.github.com/repos/espressif/esp-idf/compare/a3927641bfd0aedfaab3514cba3d7fb01378ce4d...2c211b236707889e8400c4dc5644dd5c4ee071e0) reports ahead 195 / behind 0, establishing inclusion of the merge.
|
||||
|
||||
### Exact published source/ABI delta
|
||||
|
||||
The fix changes four paths:
|
||||
|
||||
| Path relative to IDF | Published change |
|
||||
| --- | --- |
|
||||
| `components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h` | `wpa_funcs.wpa_ap_get_wpa_ie`: `uint8_t *(*)(uint8_t *len)` becomes `uint8_t *(*)(size_t *len)` |
|
||||
| `components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c` | Matching callback implementation takes `size_t *ie_len`; assignment from `hapd->wpa_auth->wpa_ie_len` remains |
|
||||
| `components/esp_wifi/lib` | Submodule `d7373a90dc3f0be841b29911e33de5f99988dbff` → **`a4e903fe43bf09a95022f9802db43d39740ccc0b`** |
|
||||
| `components/esp_wifi/include/esp_wifi_types_generic.h` | Two SAE PWE documentation comments record default `WPA3_SAE_PWE_BOTH`; not structure-layout changes |
|
||||
|
||||
The installed header and callback still take `uint8_t *`; `esp_wpa_main.c` registers the callback for SoftAP. The callback slot/pointer size need not change for this to be an ABI break: the width and interpretation of the pointed-to output change (one byte versus a `size_t`, four bytes on ESP32-S3). Mixed old/new caller and callee can corrupt storage or leave an incorrect length. Casting the callback does not fix the contract. Both sides, including the opaque caller, must agree.
|
||||
|
||||
The visible C edits are the length correction/interface marker. **The management-encryption fix is in the vendor binary update, not a public C hunk that can be copied into this application.**
|
||||
|
||||
### Bundle identity: baseline, fix point, release point are different
|
||||
|
||||
- Installed baseline matches Wi-Fi submodule **`8a1b7bbc00e895d040c5c9a6fb9d1db2bbfc7958`** from IDF v5.5, not the fix's immediate predecessor `d7373a90…`.
|
||||
- The exact fix-point bundle is **`a4e903fe43bf09a95022f9802db43d39740ccc0b`**.
|
||||
- The complete 5.5.3 release points to **`e0befaa593277b4e80726079fbd521b4681754c2`**, verified again at the immutable release commit. [Wi-Fi comparison](https://api.github.com/repos/espressif/esp32-wifi-lib/compare/a4e903fe43bf09a95022f9802db43d39740ccc0b...e0befaa593277b4e80726079fbd521b4681754c2) reports ahead 3 / behind 0. Do not call the fix-point bundle the final 5.5.3 bundle.
|
||||
|
||||
Calculated Git blob SHA-1 (`blob <length>\0` plus bytes) of **all seven installed S3 archives** and compared against official metadata for [baseline](https://api.github.com/repos/espressif/esp32-wifi-lib/contents/esp32s3?ref=8a1b7bbc00e895d040c5c9a6fb9d1db2bbfc7958), [fix point](https://api.github.com/repos/espressif/esp32-wifi-lib/contents/esp32s3?ref=a4e903fe43bf09a95022f9802db43d39740ccc0b), and [release point](https://api.github.com/repos/espressif/esp32-wifi-lib/contents/esp32s3?ref=e0befaa593277b4e80726079fbd521b4681754c2):
|
||||
|
||||
| Archive | Installed = baseline Git blob | Fix-point Git blob | 5.5.3-release Git blob |
|
||||
| --- | --- | --- | --- |
|
||||
| `libcore.a` | `ec2e76caad4c29ee92ec45432e0dc3bf946ea4c7` | `af63b0f8254bde084b2f474cdba1c1d63b4ae750` | `9f7b14a8bf6eec64973da8adc65d35b5ba9bee49` |
|
||||
| `libespnow.a` | `d4925dc7f73173077e8ed58abbbe5a59b5bb7be5` | `da0537c3f754a9d638fbecd4f4d806bfa66316b4` | `132b4f67e339ca2081d2add91c14eefa39476ff9` |
|
||||
| `libmesh.a` | `5cd43bf5a0a0388fb6f093a154401f00227d9570` | `6ae30c90ba59ab949e98db89b6b51daab434a5a0` | `2e9dc1c8c7afbf033337b4175032e9b1161e3262` |
|
||||
| `libnet80211.a` | `6ee20bec142638dc0a442b9642de9fa62db6ddd0` | `8ce8a2a6a9a753e31508bd609bf0a0d1d44c332f` | `2800d447ec385d33869373696ba8191292647694` |
|
||||
| `libpp.a` | `c9fc98fe529c9ff2891839fdb9a41b1189c03c49` | `536fd25e30a1be76ff122d93e7481f1da22e6892` | `8944bcad7371621045f376cf74c62fde6f368cbb` |
|
||||
| `libsmartconfig.a` | `31518eb9336f07de14b4a71c0389217a54ed3ec7` | `19e8274e0961fdf88351a0dec6e0d696b8ee2b94` | `4dc759e25617aa00b9e12887fd092a1d5780a170` |
|
||||
| `libwapi.a` | `0604ff76aa9134b475daf08496e03350d5ef8f09` | `e43863a22e93990b8a4deb7c4efc8e4c27566789` | `65655b6feab0572a6e8a1200946d53a21f3f4722` |
|
||||
|
||||
All installed archives match baseline; all differ from both newer bundles. This is artifact identity, not proof that every archive is linked or contains the defect. Remote binaries were **not downloaded**. Git IDs are provenance identifiers, not independently measured remote SHA-256 digests or a vendor signature verification.
|
||||
|
||||
The release API advertises the submodule-inclusive `esp-idf-v5.5.3.zip`, asset ID **357881088**, SHA-256 **`d16e6b57b1df1a523aff6a1d0ef2f99cf058cb71e73f694429769eaaea171428`**. This is vendor-published metadata, not a locally verified download. The release is marked `immutable=false`; therefore pin the resolved commit/submodules and verify artifact hashes, not merely a tag URL. Espressif warns that GitHub's automatic source archives omit submodules; those are not complete SDK inputs.
|
||||
|
||||
## PMF/WPA3 mitigation assessment
|
||||
|
||||
Both the [v5.5 documentation source](https://raw.githubusercontent.com/espressif/esp-idf/v5.5/docs/en/api-guides/wifi-security.rst) and [immutable candidate documentation](https://raw.githubusercontent.com/espressif/esp-idf/2c211b236707889e8400c4dc5644dd5c4ee071e0/docs/en/api-guides/wifi-security.rst), plus the installed type header, establish:
|
||||
|
||||
- `pmf_cfg.capable` is deprecated, effectively true internally. Setting it false is **not a supported PMF-off mitigation**.
|
||||
- `required=false` permits optional PMF; it does not disable it.
|
||||
- `esp_wifi_disable_pmf_config()` is the documented optional-PMF disable API. Disabling PMF on WPA3 or WPA2/WPA3 mixed SoftAP returns an error.
|
||||
- WPA3 mandates PMF. WPA2-only mode by itself still does not prove PMF is disabled.
|
||||
|
||||
**No issue-specific official workaround was found in the inspected release/fix text.** The API documentation explains general configuration, not an endorsement that disabling PMF safely mitigates all defects in this change. The trigger supplies a rationale that actually eliminating `bss->pmf_enable` could avoid the described encryption path; the opaque implementation and lifecycle have not been proved to honor that invariant across existing associations/restarts.
|
||||
|
||||
Consequently, a WPA2-only plus explicit PMF-disable implementation is at most an **unapproved conditional workaround candidate**. It would remove SAE benefits and management-frame protection, require explicit owner acceptance, documented threat trade-offs, correct manager-owned stop/reconfigure/start sequencing, checked API results, and target evidence that negotiated PMF cannot reappear across APSTA/fallback transitions. It would not repair the RSN-IE allocation defect. Do not implement it as a silent downgrade, global WPA3 Kconfig removal, or station-profile change.
|
||||
|
||||
If operational containment is urgently required before an update, the owner may explicitly choose existing AP-off policy (removes the described AP response path) or Wi-Fi-off (removes radio service), accepting lost network/fallback access. These are feature-withdrawal containment options, **not vendor-certified workarounds or a source fix**. Preserve and verify UART0/native USB access first; do not automatically persist policy changes. No containment action was taken here.
|
||||
|
||||
## What a coherent replacement entails
|
||||
|
||||
### Preferred: complete vendor maintenance release
|
||||
|
||||
Use all IDF source, private/public headers, Wi-Fi/PHY/coexistence libraries and other submodules from one resolved release, with its toolchain and Python/build requirements. Do not install final-release blobs alongside fix-point-only C edits or mix two release revisions. Preserve the application security policies separately through audited overrides.
|
||||
|
||||
Verified local integration constraints:
|
||||
|
||||
| Input | Current state | Required next action |
|
||||
| --- | --- | --- |
|
||||
| `platformio.ini` | `platformio/espressif32@6.12.0`, ESP-IDF, ESP32-S3 N16R8 | Select a supported pinned platform/framework/toolchain combination; no floating update |
|
||||
| Installed platform `platform.json` | Framework range `~3.50500.0`; installed `framework-espidf/package.json` is `3.50500.0` | Does not select 5.5.3; establish package provenance and adapter support before proposing a package override |
|
||||
| Installed Xtensa toolchain | `14.2.0+20241119` | Candidate's immutable [`tools/tools.json`](https://github.com/espressif/esp-idf/blob/2c211b236707889e8400c4dc5644dd5c4ee071e0/tools/tools.json) recommends **`esp-14.2.0_20251107`**; same GCC major/minor is not identical tooling or compatibility evidence |
|
||||
| `tools/security_overrides.py` | `verify_version()` requires exactly **5.5.0**; each original source has an exact SHA-256 and executable edit contract | A full upgrade intentionally fails until semantic re-audit/rebase; never make the version range permissive just to build |
|
||||
| `cmake/security_overrides.cmake` | Exact-once source replacement, nested mbedTLS ownership checks, source-property preservation, forced PUBLIC wolfSSH ABI overlay | Revalidate generated and actual compiler inputs, target identities and include order |
|
||||
|
||||
No known-good PlatformIO 6.12 + IDF 5.5.3 package combination was established here. The installed adapter was read only for package/version/toolchain selection references, not certified for the candidate. A raw IDF Git checkout is not automatically a PlatformIO framework package. Prefer a supported platform release carrying a suitable complete SDK; otherwise a maintained, exact-pinned framework/toolchain packaging effort is a separate explicit implementation deliverable.
|
||||
|
||||
Re-audit all existing corrections against candidate originals, not only Wi-Fi: HTTPS cleanup/private-key release, HTTPD scratch/null-read handling, server-local TLS policy, DHCP bounds, TLS EMS failure, X.509 allocation failure; retain/revalidate wolfSSH password/parser/ordering/ABI changes and wolfCrypt settings. Mark each correction **retained, rebased, or superseded with source/test evidence**. Upstream fixes do not automatically supersede local lifetime/policy additions in the same source. Also retain silent-panic/no-coredump and heap-wipe policy guards. The durable architecture's older four-correction summary is not the complete current inventory; the current generator and code map are authoritative.
|
||||
|
||||
### Contingency: vendor-supported backport onto 5.5.0
|
||||
|
||||
The smallest **published delta against the fix's own parent** is the four-path change above. The smallest **proven safe delta against this project's 5.5.0** is **not established**.
|
||||
|
||||
A candidate backport must at least pair both callback changes with one complete ESP32-S3 Wi-Fi archive set from the same vendor revision, retain the vendor patch/provenance, and establish compatibility with all private Wi-Fi headers, OS adapter tables, supplicant structures/callbacks, PHY/coexistence interfaces, configuration ABI and toolchain. All seven S3 archives form the conservative replacement set; do not mix old optional archives with new ones simply because current linking omits them. This minimum set is necessary for coherent provenance, **not sufficient proof** of cross-baseline compatibility.
|
||||
|
||||
Ask Espressif for a supported 5.5.0 backport bundle or an explicit compatibility statement with its required source/header/library/toolchain prerequisites, confirming both management-frame and IE-length corrections. The immediate-parent blob pointer differs from baseline; successful linking alone cannot establish binary layout/calling-contract compatibility. Without that evidence or a complete audited dependency closure, choose the full-release path rather than treating the two C signature edits plus blobs as approved.
|
||||
|
||||
## Bounded implementation and acceptance plan
|
||||
|
||||
1. **Compatibility/provenance gate first, no code change yet.** Resolve one deployment release and PlatformIO delivery combination. Record immutable SDK commit, recursive submodule pins, package/toolchain identities and SHA-256 manifests. Confirm vendor inclusion of the fix. Obtain missing packaging/ABI evidence; if unavailable, report the blocker instead of guessing a framework URL or transplanting archives.
|
||||
2. **Prepare isolated inputs after approval.** Do not modify the user's shared installed SDK. Obtain complete vendor inputs in a dedicated location, verify provenance/hashes, and compare only relevant integration surfaces and protected-source entries. Keep existing managed components pinned where compatible; explicitly review any resolver-required changes. Review license/notice/source-delivery updates for new radio/SDK artifacts.
|
||||
3. **Rebase security contracts.** Implement a reviewed per-entry disposition and exact new source pins, retain fail-closed guards, and verify every generated override/forced header reaches the actual targets. Add a bundle-consistency check for the selected SDK and archive identities so an old/new mixed bundle fails deterministically. Do not enable unrelated features, regenerate web assets, migrate credentials, or alter partitions.
|
||||
4. **Build/host gates in the later implementation task only.** Clean candidate build with the selected toolchain; inspect linked archive paths/map, generated configuration and actual compilation inputs. Run SDK override, crypto, parser, ordering/interop, authentication/protocol, build-policy and notice validations as applicable to the resolved input changes. Record sizes and compare resource budgets; a host pass is not radio validation. Existing historical passes do not validate this candidate.
|
||||
5. **Target gates under explicit device authorization.** Exercise WPA2 with/without negotiated PMF and WPA3 SoftAP; authenticate/reassociate after keys are installed; cover PMF optional/required behavior, four clients, disconnect/reconnect and AP/STA/APSTA/fallback transitions. Obtain vendor regression guidance or an authorized isolated-radio reproduction for the exact trigger; ordinary reconnect tests alone do not prove vulnerability closure. Stress concurrent HTTPS/WebSocket/SSH and verify bounded recovery, heap/stack reserve, queue behavior, no secret/crash-memory disclosure, and unchanged broker isolation. Verify UART0 recovery and native USB UART1 operation while Wi-Fi fails/stops/restarts. Include persisted-config reboot tests without erasure.
|
||||
6. **Release gate.** Keep PMF/WPA3 security at least unchanged. Record exact artifacts and test evidence, retain a controlled recovery image and configuration backup procedure, and mark the old image as still exposed rather than calling rollback a security fix. No flash erase, upload, pin changes or deployment until separately authorized. Update durable project memory only when implemented contracts/ownership genuinely change.
|
||||
|
||||
## Open evidence and review boundary
|
||||
|
||||
- **Resolved:** official fix IDs/description; exact visible ABI delta; all seven installed S3 archives match baseline; distinct fix and final-release bundles; release/fix ancestry; current application configuration; PMF-disable restrictions; exact-IDF/hash guards; recommended versus installed toolchain mismatch.
|
||||
- **Unresolved:** supported PlatformIO delivery for the chosen maintenance release; full private ABI prerequisite closure for a 5.5.0 transplant; exact PHY/coexistence submodule pins for the candidate in this review; complete tool/Python/managed-component compatibility; opaque binary implementation correctness; packet-level attacker requirements; target/runtime/resource evidence; latest deployment-release selection.
|
||||
- A follow-up GitHub API request for candidate `components/esp_phy/lib` returned **HTTP 403 rate limit exceeded**. The sequential metadata query stopped there, so PHY/coexistence pins were not established; no inference was drawn from that failure. Already completed Wi-Fi/release/ancestry queries succeeded. Official raw documentation remained available. Do not treat this partial metadata boundary as a compatibility clearance.
|
||||
- Validation performed here: read-only local source/package/config inspection; local seven-archive Git-blob hashing; official patch/release/tag/content/comparison/toolchain/documentation reads. No broad repeated dependency audit, binary download, PlatformIO invocation, build, test suite, device operation, configuration edit, or asset generation. Documentation links/provenance do not constitute signed-vendor attestation, exhaustive security certification, or Phase 9 sign-off.
|
||||
@@ -36,8 +36,25 @@ with tempfile.TemporaryDirectory(prefix="admin-console-boundary-") as directory:
|
||||
"-o", str(path / "certificate")], check=True, timeout=30)
|
||||
subprocess.run([str(path / "certificate")], check=True, timeout=10)
|
||||
ssh = (ROOT / "src/ssh_transport.c").read_text()
|
||||
adapter = ssh[ssh.index("static admin_ssh_console_token_t admin_console_token("):
|
||||
ssh.index("static void *ssh_malloc(")]
|
||||
# Bound the adapter by its own functions, not the allocator helpers now
|
||||
# owned by ssh_memory.c. Fail closed if the reviewed source layout changes.
|
||||
adapter_markers = (
|
||||
"static admin_ssh_console_token_t admin_console_token(",
|
||||
"static void publish_slot(",
|
||||
"static size_t admin_console_snapshot_index_locked(",
|
||||
"static bool admin_console_is_current(",
|
||||
"static bool admin_console_drained(",
|
||||
"static esp_err_t admin_console_perform(",
|
||||
"static const admin_console_owner_t s_admin_console_owner = {",
|
||||
"static bool consume_external_close(",
|
||||
)
|
||||
positions = []
|
||||
for marker in adapter_markers:
|
||||
assert ssh.count(marker) == 1, f"SSH adapter marker missing/ambiguous: {marker}"
|
||||
positions.append(ssh.index(marker))
|
||||
assert positions == sorted(positions), "SSH adapter source order changed"
|
||||
adapter_end = ssh.index("\n}", positions[-1]) + len("\n}")
|
||||
adapter = ssh[positions[0]:adapter_end]
|
||||
unit = ((ROOT / "tests/admin_console_boundary/fakes.h").read_text()
|
||||
+ strip_includes(header) + "\n" + strip_includes(source)
|
||||
+ (ROOT / "tests/admin_console_boundary/adapter.c").read_text()
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Offline orchestrator fixtures only; never invokes pio or existing suites."""
|
||||
import contextlib
|
||||
import importlib.util
|
||||
import io
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
spec = importlib.util.spec_from_file_location('phase9', ROOT / 'tools/validate_phase9.py')
|
||||
runner = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = runner
|
||||
spec.loader.exec_module(runner)
|
||||
|
||||
|
||||
class ValidationTests(unittest.TestCase):
|
||||
def options(self, *args):
|
||||
return runner.parser().parse_args(args)
|
||||
|
||||
def command(self, code, name='fixture', timeout=3, required=()):
|
||||
return runner.Command(name, (sys.executable, '-c', code), timeout, required)
|
||||
|
||||
def execute(self, commands, **kwargs):
|
||||
output = io.StringIO()
|
||||
with contextlib.redirect_stdout(output):
|
||||
code = runner.execute(commands, ROOT, **kwargs)
|
||||
return code, output.getvalue()
|
||||
|
||||
def test_default_exact_scope_and_real_paths(self):
|
||||
commands = runner.plan(self.options())
|
||||
self.assertEqual(len(commands), 23)
|
||||
self.assertEqual({c.name for c in commands}, {
|
||||
'security_build_policy', 'ssh_auth_policy', 'ssh_auth_transport', 'hidden_input',
|
||||
'ssh_memory', 'sdk_security_overrides', 'wolfssh_auth_contract', 'ssh_protocol_policy',
|
||||
'wolf_crypto_policy', 'wolfssh_parser_contract', 'wolfssh_order_contract', 'release_notices',
|
||||
'ssh_management', 'admin_console_boundary', 'admin_ssh_policy', 'web_admin_transport',
|
||||
'web_admin_tickets', 'web_httpd_idle', 'web_cookie_auth:base', 'web_cookie_auth:admin',
|
||||
'web_cookie_auth:accounts', 'web_cookie_auth:ssh', 'web_cookie_auth:lifecycle'})
|
||||
for command in commands:
|
||||
self.assertTrue((ROOT / command.argv[2]).is_file())
|
||||
self.assertEqual(command.argv[:2], (sys.executable, '-B'))
|
||||
self.assertEqual(command.timeout, 180)
|
||||
self.assertFalse({'--interop', '--host-only', '--candidate', '--target-contracts',
|
||||
'--pio-adapter', 'pio', '--admission'} & set(command.argv))
|
||||
by_name = {c.name: c for c in commands}
|
||||
database = str(runner.BUILD / 'compile_commands.json')
|
||||
for name in ('wolfssh_auth_contract', 'ssh_protocol_policy', 'wolf_crypto_policy'):
|
||||
self.assertEqual(by_name[name].argv[3:], ('--compile-commands', database))
|
||||
self.assertEqual(by_name['sdk_security_overrides'].argv[3:], ('--build-dir', str(runner.BUILD)))
|
||||
self.assertEqual(by_name['security_build_policy'].argv[3:],
|
||||
('--sdkconfig-header', str(runner.BUILD / 'config/sdkconfig.h')))
|
||||
|
||||
def test_opt_ins_independent_and_timeout(self):
|
||||
commands = runner.plan(self.options('--build', '--timeout', '4', '--build-timeout', '5'))
|
||||
self.assertEqual(commands[0], runner.Command('build', ('pio', 'run'), 5))
|
||||
self.assertTrue(all(c.timeout == 4 for c in commands[1:]))
|
||||
self.assertFalse(any('--interop' in c.argv for c in commands))
|
||||
commands = runner.plan(self.options('--interop'))
|
||||
self.assertNotIn('build', [c.name for c in commands])
|
||||
self.assertEqual([c.name for c in commands if '--interop' in c.argv], ['wolfssh_order_contract'])
|
||||
|
||||
def test_bad_timeout(self):
|
||||
for value in ('0', '-1', 'nan', 'inf', '3601', 'junk'):
|
||||
with contextlib.redirect_stderr(io.StringIO()), self.assertRaises(SystemExit):
|
||||
self.options('--timeout', value)
|
||||
|
||||
def test_dry_run_never_launches_or_checks_prerequisites(self):
|
||||
with patch.object(runner, 'run_command', side_effect=AssertionError('executed')):
|
||||
code, output = self.execute(runner.plan(self.options('--build', '--interop')), dry_run=True)
|
||||
self.assertEqual(code, 0)
|
||||
self.assertIn('PLAN build', output)
|
||||
self.assertNotIn('PASS ', output)
|
||||
with patch.object(runner, 'run_command', side_effect=AssertionError('executed')):
|
||||
with contextlib.redirect_stdout(io.StringIO()) as output:
|
||||
self.assertEqual(runner.main(['--dry-run']), 0)
|
||||
self.assertIn('SKIP optional build', output.getvalue())
|
||||
self.assertIn('SKIP optional interop', output.getvalue())
|
||||
|
||||
def test_collect_failure_and_failfast_skip(self):
|
||||
commands = [self.command('raise SystemExit(7)', 'bad'), self.command('pass', 'good')]
|
||||
code, output = self.execute(commands)
|
||||
self.assertEqual(code, 1)
|
||||
self.assertIn('FAIL bad: exit 7', output)
|
||||
self.assertIn('PASS good', output)
|
||||
code, output = self.execute(commands, fail_fast=True)
|
||||
self.assertEqual(code, 1)
|
||||
self.assertIn('SKIP good', output)
|
||||
self.assertNotIn('PASS good', output)
|
||||
|
||||
def test_failed_build_blocks_stale_artifact_checks(self):
|
||||
code, output = self.execute([self.command('raise SystemExit(1)', 'build'), self.command('pass', 'host')])
|
||||
self.assertEqual(code, 1)
|
||||
self.assertIn('SKIP host', output)
|
||||
|
||||
def test_prerequisites(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
missing = Path(tmp) / 'missing'
|
||||
commands = [runner.Command('missing-executable', (str(missing),), 1),
|
||||
self.command('raise AssertionError()', 'missing-input', required=(missing,))]
|
||||
code, output = self.execute(commands)
|
||||
self.assertEqual(code, 1)
|
||||
self.assertIn('PREREQ missing-executable', output)
|
||||
self.assertIn('PREREQ missing-input', output)
|
||||
|
||||
def test_environment_and_literal_argv(self):
|
||||
with tempfile.TemporaryDirectory(prefix='phase9 fixture ') as tmp:
|
||||
script = Path(tmp) / 'fixture ; literal.py'
|
||||
script.write_text('import os, sys\nassert os.environ["CCACHE_DISABLE"] == "1"\n'
|
||||
'assert os.environ["PHASE9_FIXTURE"] == "preserved"\n'
|
||||
'assert sys.argv[1] == "a ; $(not-a-command)"\n')
|
||||
command = runner.Command('literal', (sys.executable, str(script), 'a ; $(not-a-command)'), 3)
|
||||
with patch.dict(os.environ, {'PHASE9_FIXTURE': 'preserved', 'CCACHE_DISABLE': '0'}):
|
||||
self.assertEqual(self.execute([command])[0], 0)
|
||||
self.assertEqual(os.environ['CCACHE_DISABLE'], '0')
|
||||
|
||||
def test_timeout_kills_descendant_and_collects(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
marker = Path(tmp) / 'should-not-exist'
|
||||
child = f'import time; from pathlib import Path; time.sleep(1); Path({str(marker)!r}).touch()'
|
||||
parent = f'import subprocess, sys, time; subprocess.Popen([sys.executable, "-c", {child!r}]); time.sleep(10)'
|
||||
started = time.monotonic()
|
||||
code, output = self.execute([self.command(parent, timeout=0.25), self.command('pass', 'next')])
|
||||
self.assertLess(time.monotonic() - started, 4)
|
||||
self.assertEqual(code, 1)
|
||||
self.assertIn('TIMEOUT fixture', output)
|
||||
self.assertIn('PASS next', output)
|
||||
time.sleep(1.1)
|
||||
self.assertFalse(marker.exists())
|
||||
|
||||
def test_streams_not_captured_and_stdin_closed(self):
|
||||
with patch.object(runner.subprocess, 'Popen') as popen, patch.object(runner.os, 'killpg'):
|
||||
popen.return_value.wait.return_value = 0
|
||||
code, _ = self.execute([self.command('pass')])
|
||||
self.assertEqual(code, 0)
|
||||
kwargs = popen.call_args.kwargs
|
||||
self.assertNotIn('stdout', kwargs)
|
||||
self.assertNotIn('stderr', kwargs)
|
||||
self.assertNotIn('shell', kwargs)
|
||||
self.assertEqual(kwargs['stdin'], subprocess.DEVNULL)
|
||||
self.assertTrue(kwargs['start_new_session'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,7 +1,7 @@
|
||||
# Release notice tool tests
|
||||
|
||||
Run `python3 tests/release_notices/run.py` from the repository root. Uses Python's
|
||||
standard library and isolated temporary fixtures only; no managed package,
|
||||
standard library, isolated temporary fixtures and checked-in icon evidence; no managed package,
|
||||
SDK, toolchain, PlatformIO, network, or device is required. Linux/POSIX path and
|
||||
descriptor semantics match the notice tool.
|
||||
|
||||
@@ -10,8 +10,25 @@ across moved roots/changed mtimes; missing, empty, changed and oversized inputs;
|
||||
source body drift outside excerpts; bounds/schema; traversal and symlinks in
|
||||
input/output ancestry; FIFOs/directories; existing user-data preservation;
|
||||
explicit output requirement; unlisted secret/config/build exclusion; incomplete
|
||||
write behavior; and success/failure CLI exits.
|
||||
write behavior; and success/failure CLI exits. Additional offline icon tests check
|
||||
exact upstream SHA-256/Git blob identities, release and author metadata, absence
|
||||
of NOTICE paths in the complete pinned tree, actual retained SVG coordinates,
|
||||
USB mockup transforms, manual firmware row bytes, and catalog inclusion. A
|
||||
changed coordinate or added transform is rejected; differing mockup Wi-Fi and
|
||||
unproven manual rasterization are not described as exact upstream matches.
|
||||
|
||||
Real installed-input assembly and recipient delivery are separate checks; see
|
||||
`docs/release_packaging.md`. Passing these tests is not license clearance or
|
||||
proof of corresponding-source compliance.
|
||||
Real installed-input assembly is an explicit, separate offline check:
|
||||
|
||||
```sh
|
||||
python3 tests/release_notices/actual_bundle.py \
|
||||
--sdk-root /home/mscholz/.platformio/packages/framework-espidf \
|
||||
--toolchain-root /home/mscholz/.platformio/packages/toolchain-xtensa-esp-elf
|
||||
```
|
||||
|
||||
Use literal paths for the reviewed installed snapshot. This invokes the real
|
||||
CLI twice with fresh temporary outputs outside input roots, verifies catalog
|
||||
identity, every manifest entry and payload hash/size, absence of extra files,
|
||||
and identical relative names/bytes across outputs. Temporary outputs are removed
|
||||
after the check. No fetch, dependency installation, build or device access.
|
||||
Recipient delivery remains separate; see `docs/release_packaging.md`. Passing
|
||||
these tests is not license clearance or corresponding-source compliance.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
"""Assemble two real bundles in temporary directories and verify every byte."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
PROJECT = Path(__file__).absolute().parents[2]
|
||||
|
||||
|
||||
def contents(root):
|
||||
return {p.relative_to(root).as_posix(): p.read_bytes()
|
||||
for p in root.rglob("*") if p.is_file()}
|
||||
|
||||
|
||||
def validate(files, catalog_data):
|
||||
manifest = json.loads(files["manifest.json"])
|
||||
catalog = json.loads(catalog_data)
|
||||
assert manifest["catalog_sha256"] == hashlib.sha256(catalog_data).hexdigest()
|
||||
assert manifest["snapshot"] == catalog["snapshot"]
|
||||
assert len(manifest["inputs"]) == len(catalog["inputs"])
|
||||
expected_entries = {(x["root"], x["path"]): x for x in catalog["inputs"]}
|
||||
expected_files = {"manifest.json"}
|
||||
seen = set()
|
||||
for entry in manifest["inputs"]:
|
||||
key = (entry["root"], entry["path"])
|
||||
assert key not in seen
|
||||
seen.add(key)
|
||||
original = expected_entries[key]
|
||||
assert {k: entry[k] for k in original} == original
|
||||
path = "inputs/" + entry["root"] + "/" + entry["path"]
|
||||
if entry["range"] is not None:
|
||||
path += ".notice.txt"
|
||||
assert entry["output"] == path
|
||||
expected_files.add(path)
|
||||
data = files[path]
|
||||
assert len(data) == entry["output_size"]
|
||||
assert hashlib.sha256(data).hexdigest() == entry["output_sha256"]
|
||||
assert seen == set(expected_entries)
|
||||
assert len(manifest["generated"]) == 1
|
||||
assert manifest["generated"][0]["path"] == "README.txt"
|
||||
for entry in manifest["generated"]:
|
||||
expected_files.add(entry["path"])
|
||||
data = files[entry["path"]]
|
||||
assert len(data) == entry["size"]
|
||||
assert hashlib.sha256(data).hexdigest() == entry["sha256"]
|
||||
assert set(files) == expected_files, "unexpected or missing bundle files"
|
||||
return len(manifest["inputs"])
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--sdk-root", type=Path, required=True)
|
||||
parser.add_argument("--toolchain-root", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
catalog = (PROJECT / "third_party/release-notices/inputs.json").read_bytes()
|
||||
with tempfile.TemporaryDirectory(prefix="actual-release-notices-") as temporary:
|
||||
base = Path(temporary)
|
||||
outputs = []
|
||||
for name in ("first", "second"):
|
||||
output = base / name
|
||||
subprocess.run([
|
||||
sys.executable, str(PROJECT / "tools/release_notices.py"),
|
||||
"--sdk-root", str(args.sdk_root),
|
||||
"--toolchain-root", str(args.toolchain_root), "--output", str(output),
|
||||
], check=True, timeout=60)
|
||||
files = contents(output)
|
||||
count = validate(files, catalog)
|
||||
outputs.append(files)
|
||||
assert outputs[0] == outputs[1], "bundle names/bytes differ"
|
||||
files = outputs[0]
|
||||
print(f"PASS: two actual bundles; {count} inputs; {len(files)} files; "
|
||||
f"{sum(map(len, files.values()))} bytes; all payloads verified and deterministic")
|
||||
print("manifest SHA-256:", hashlib.sha256(files["manifest.json"]).hexdigest())
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,173 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
"""Offline checks of actual project icon derivatives, not a raster generator."""
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import unittest
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
PROJECT = Path(__file__).absolute().parents[2]
|
||||
ICONS = PROJECT / "third_party/material-design-icons"
|
||||
UPSTREAM = ICONS / "upstream-7.4.47"
|
||||
COMMIT = "9e04201d4557e729822fb57f62a316c3dea1d4a8"
|
||||
TAG = "5edde266e281d26a03dcfa89fb651183cbab0f2e"
|
||||
SVG = "{http://www.w3.org/2000/svg}"
|
||||
PINS = {
|
||||
"usb.svg": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
|
||||
"wifi-strength-4.svg": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
|
||||
"LICENSE": "f3bc8715bad84b26396bb42d2abd11f919cf58163be158eab79ecbaabf84cdf2",
|
||||
"package.json": "ffeb0e4cc17b4cb124408bd0068667b9a35e985971792978e40d4cd69c7ca8b7",
|
||||
"README.md": "db5acca5eb4e2c9113c3408ed1ae7d17c76f653340a5d4e3f8a58d2bcdf50b7d",
|
||||
"meta.json": "ed9d7c7a5ca3ce7dee023fb6c1d62715dbec5b27503d268cbc10b9c8894ef3e8",
|
||||
"tag.json": "f02dc7fc1e862c344c66877828b1914543f0656db8544c05a1ed3b7d990b6d75",
|
||||
"tree.json": "b75710c2b9ee4d0ad3f79016e9767a85103f16df6cbbe5f0629efc3b470d1569",
|
||||
"Apache-2.0.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
|
||||
}
|
||||
|
||||
|
||||
def load(name):
|
||||
return json.loads((UPSTREAM / name).read_bytes())
|
||||
|
||||
|
||||
def geometry(element):
|
||||
"""Strict comparison: no coordinate normalization or hidden transforms."""
|
||||
if set(element.attrib) != {"id", "viewBox"}:
|
||||
raise ValueError("unexpected root/symbol geometry attributes")
|
||||
if element.attrib["viewBox"] != "0 0 24 24":
|
||||
raise ValueError("unexpected viewBox")
|
||||
if len(element) != 1 or element[0].tag != SVG + "path":
|
||||
raise ValueError("expected exactly one path")
|
||||
if set(element[0].attrib) != {"d"}:
|
||||
raise ValueError("unexpected path geometry attributes")
|
||||
return element[0].attrib["d"]
|
||||
|
||||
|
||||
class IconProvenanceTests(unittest.TestCase):
|
||||
def test_download_pins_and_git_blobs(self):
|
||||
records = load("downloads.json")
|
||||
self.assertEqual({r["path"] for r in records}, set(PINS))
|
||||
self.assertEqual(len(records), len(PINS))
|
||||
tree = {x["path"]: x for x in load("tree.json")["tree"]}
|
||||
for record in records:
|
||||
name = record["path"]
|
||||
with self.subTest(name=name):
|
||||
data = (UPSTREAM / name).read_bytes()
|
||||
self.assertEqual(len(data), record["size"])
|
||||
self.assertEqual(hashlib.sha256(data).hexdigest(), PINS[name])
|
||||
self.assertEqual(record["sha256"], PINS[name])
|
||||
if name not in ("tag.json", "tree.json", "Apache-2.0.txt"):
|
||||
path = "svg/" + name if name.endswith(".svg") else name
|
||||
self.assertEqual(record["url"],
|
||||
"https://raw.githubusercontent.com/Templarian/"
|
||||
f"MaterialDesign-SVG/{COMMIT}/{path}")
|
||||
self.assertEqual(tree[path]["size"], len(data))
|
||||
blob = b"blob " + str(len(data)).encode() + b"\0" + data
|
||||
self.assertEqual(hashlib.sha1(blob).hexdigest(), tree[path]["sha"])
|
||||
for name in ("usb.svg", "wifi-strength-4.svg", "LICENSE"):
|
||||
self.assertEqual((ICONS / name).read_bytes(), (UPSTREAM / name).read_bytes())
|
||||
|
||||
def test_release_license_authors_and_notice_scope(self):
|
||||
tag = load("tag.json")
|
||||
self.assertEqual(tag["sha"], TAG)
|
||||
self.assertEqual(tag["tag"], "v7.4.47")
|
||||
self.assertEqual(tag["object"]["sha"], COMMIT)
|
||||
self.assertEqual(tag["object"]["type"], "commit")
|
||||
self.assertFalse(tag["verification"]["verified"])
|
||||
package = load("package.json")
|
||||
self.assertEqual((package["name"], package["version"], package["license"]),
|
||||
("@mdi/svg", "7.4.47", "Apache-2.0"))
|
||||
expected = {
|
||||
"usb": ("Google", "25033E0B-3AD4-414D-9972-559F2690FC1D", "1.5.54"),
|
||||
"wifi-strength-4": ("Simran", "41B86B22-7245-4A97-9BAA-3E9EBD44CEB0", "2.3.50"),
|
||||
}
|
||||
selected = [x for x in load("meta.json") if x["name"] in expected]
|
||||
self.assertEqual(len(selected), 2)
|
||||
for icon in selected:
|
||||
self.assertEqual((icon["author"], icon["id"], icon["version"]), expected[icon["name"]])
|
||||
self.assertNotIn("license", icon)
|
||||
tree = load("tree.json")
|
||||
self.assertFalse(tree["truncated"])
|
||||
self.assertFalse([x["path"] for x in tree["tree"] if "notice" in x["path"].lower()])
|
||||
|
||||
def test_exact_retained_geometry_and_negative_changes(self):
|
||||
for name in ("usb.svg", "wifi-strength-4.svg"):
|
||||
original = ET.parse(UPSTREAM / name).getroot()
|
||||
local = ET.parse(ICONS / name).getroot()
|
||||
self.assertEqual(geometry(local), geometry(original))
|
||||
changed = copy.deepcopy(local)
|
||||
changed[0].set("d", changed[0].get("d").replace("M", "M0,0L", 1))
|
||||
self.assertNotEqual(geometry(changed), geometry(original))
|
||||
for node in (0, None):
|
||||
changed = copy.deepcopy(local)
|
||||
(changed if node is None else changed[node]).set("transform", "translate(1 0)")
|
||||
with self.assertRaises(ValueError):
|
||||
geometry(changed)
|
||||
|
||||
def test_actual_mockup_paths_and_transforms(self):
|
||||
root = ET.parse(PROJECT / "docs/phase7c_icon_mockup.svg").getroot()
|
||||
parents = {child: parent for parent in root.iter() for child in parent}
|
||||
symbols = {x.get("id"): x for x in root.iter(SVG + "symbol")}
|
||||
self.assertEqual(geometry(symbols["usb"]), geometry(ET.parse(UPSTREAM / "usb.svg").getroot()))
|
||||
self.assertNotEqual(geometry(symbols["wifi"]),
|
||||
geometry(ET.parse(UPSTREAM / "wifi-strength-4.svg").getroot()))
|
||||
uses = [x for x in root.iter(SVG + "use") if x.get("href") == "#usb"]
|
||||
self.assertEqual([x.attrib for x in uses], [
|
||||
{"href": "#usb", "x": "150", "y": "4", "width": "30", "height": "30"},
|
||||
{"href": "#usb", "x": "8", "y": "72", "width": "30", "height": "30"},
|
||||
])
|
||||
|
||||
def ancestry(node):
|
||||
result = []
|
||||
while node in parents:
|
||||
node = parents[node]
|
||||
if "transform" in node.attrib:
|
||||
result.append(node.get("transform"))
|
||||
# Nested SVG viewports would require additional mappings.
|
||||
if node is not root:
|
||||
self.assertNotEqual(node.tag, SVG + "svg")
|
||||
return result
|
||||
|
||||
self.assertEqual(ancestry(uses[0]), [])
|
||||
self.assertEqual(parents[parents[uses[0]]].get("id"), "status")
|
||||
self.assertEqual(ancestry(uses[1]), ["translate(62 438)"])
|
||||
status_uses = [x for x in root.iter(SVG + "use") if x.get("href") == "#status"]
|
||||
self.assertEqual([x.attrib for x in status_uses], [{"href": "#status"}] * 4)
|
||||
self.assertEqual([ancestry(x) for x in status_uses], [
|
||||
["translate(62 98)"], ["translate(592 98)"],
|
||||
["translate(62 438)"], ["translate(592 438)"],
|
||||
])
|
||||
|
||||
def test_actual_manual_masks_not_inferred_rasterization(self):
|
||||
source = (PROJECT / "src/local_status_ui.c").read_text()
|
||||
self.assertIn("hand-rasterized 8x8 derivative", source)
|
||||
self.assertIn('adaptations of "wifi-strength-4"', source)
|
||||
masks = {
|
||||
"usb": "18 3c 18 5a 3e 18 3c 18",
|
||||
"wifi_full": "7e 81 3c 42 18 24 00 18",
|
||||
"wifi_three": "00 00 3c 42 18 24 00 18",
|
||||
"wifi_two": "00 00 00 00 18 24 00 18",
|
||||
"wifi_one": "00 00 00 00 00 00 00 18",
|
||||
}
|
||||
for name, expected in masks.items():
|
||||
match = re.search(r"s_icon_" + name + r"\[LOCAL_STATUS_UI_ICON_SIZE\]\s*=\s*\{([^}]+)\}", source)
|
||||
self.assertIsNotNone(match)
|
||||
actual = re.findall(r"0x([0-9a-fA-F]{2})U", match[1])
|
||||
self.assertEqual(bytes.fromhex(" ".join(actual)), bytes.fromhex(expected))
|
||||
|
||||
def test_all_icon_evidence_is_pinned_in_catalog(self):
|
||||
catalog = json.loads((PROJECT / "third_party/release-notices/inputs.json").read_bytes())
|
||||
entries = {(x["root"], x["path"]): x for x in catalog["inputs"]}
|
||||
paths = [UPSTREAM / name for name in (*PINS, "downloads.json")]
|
||||
paths += [ICONS / name for name in ("LICENSE", "usb.svg", "wifi-strength-4.svg")]
|
||||
paths += [PROJECT / "docs/icon_provenance.md"]
|
||||
for path in paths:
|
||||
with self.subTest(path=path.name):
|
||||
entry = entries[("project", path.relative_to(PROJECT).as_posix())]
|
||||
data = path.read_bytes()
|
||||
self.assertIsNone(entry["range"])
|
||||
self.assertEqual(entry["size"], len(data))
|
||||
self.assertEqual(entry["sha256"], hashlib.sha256(data).hexdigest())
|
||||
self.assertEqual(entry["output_sha256"], entry["sha256"])
|
||||
@@ -14,6 +14,8 @@ import unittest
|
||||
from unittest import mock
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
from icon_provenance import IconProvenanceTests
|
||||
|
||||
PROJECT = Path(__file__).absolute().parents[2]
|
||||
SPEC = importlib.util.spec_from_file_location("release_notices", PROJECT / "tools/release_notices.py")
|
||||
notices = importlib.util.module_from_spec(SPEC)
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,20 @@
|
||||
Pictogrammers Free License
|
||||
--------------------------
|
||||
|
||||
This icon collection is released as free, open source, and GPL friendly by
|
||||
the [Pictogrammers](http://pictogrammers.com/) icon group. You may use it
|
||||
for commercial projects, open source projects, or anything really.
|
||||
|
||||
# Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||||
Some of the icons are redistributed under the Apache 2.0 license. All other
|
||||
icons are either redistributed under their respective licenses or are
|
||||
distributed under the Apache 2.0 license.
|
||||
|
||||
# Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||||
All web and desktop fonts are distributed under the Apache 2.0 license. Web
|
||||
and desktop fonts contain some icons that are redistributed under the Apache
|
||||
2.0 license. All other icons are either redistributed under their respective
|
||||
licenses or are distributed under the Apache 2.0 license.
|
||||
|
||||
# Code: MIT (https://opensource.org/licenses/MIT)
|
||||
The MIT license applies to all non-font and non-icon files.
|
||||
@@ -0,0 +1,36 @@
|
||||
> *Note:* Please use the main [MaterialDesign](https://github.com/Templarian/MaterialDesign/issues) repo to report issues. This repo is for distribution of the SVG files only.
|
||||
|
||||
# SVG - Material Design Icons
|
||||
|
||||
SVG distribution for the [Material Design Icons](https://materialdesignicons.com).
|
||||
|
||||
```
|
||||
npm install @mdi/svg
|
||||
```
|
||||
|
||||
> Versions prior to `2.2.43` can be accessed on npm via `mdi-svg`
|
||||
|
||||
## Related Packages
|
||||
|
||||
[NPM @MDI Organization](https://npmjs.com/org/mdi)
|
||||
|
||||
- JavaScript/Typescript: [MaterialDesign-JS](https://github.com/Templarian/MaterialDesign-JS)
|
||||
- Webfont: [MaterialDesign-Webfont](https://github.com/Templarian/MaterialDesign-Webfont)
|
||||
- Font-Build: [MaterialDesign-Font-Build](https://github.com/Templarian/MaterialDesign-Font-Build)
|
||||
- Desktop Font: [MaterialDesign-Font](https://github.com/Templarian/MaterialDesign-Font)
|
||||
|
||||
## Third Party
|
||||
|
||||
For those developing third party scripts/programs/applications. Please use this repo to make sure you're using latest production ready icons.
|
||||
|
||||
Great uses of the SVG Icon distribution includes:
|
||||
|
||||
- Scripts to generate your own webfont.
|
||||
- Writing addons/plugins to integrate with applications.
|
||||
- Themes - Every theme needs icons.
|
||||
- AngularJS/Angular/React Components to work with the icons.
|
||||
|
||||
## Learn More
|
||||
|
||||
- [MaterialDesignIcons.com](https://materialdesignicons.com)
|
||||
- https://github.com/Templarian/MaterialDesign
|
||||
@@ -0,0 +1,56 @@
|
||||
[
|
||||
{
|
||||
"path": "usb.svg",
|
||||
"url": "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/9e04201d4557e729822fb57f62a316c3dea1d4a8/svg/usb.svg",
|
||||
"size": 403,
|
||||
"sha256": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22"
|
||||
},
|
||||
{
|
||||
"path": "wifi-strength-4.svg",
|
||||
"url": "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/9e04201d4557e729822fb57f62a316c3dea1d4a8/svg/wifi-strength-4.svg",
|
||||
"size": 219,
|
||||
"sha256": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f"
|
||||
},
|
||||
{
|
||||
"path": "LICENSE",
|
||||
"url": "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/9e04201d4557e729822fb57f62a316c3dea1d4a8/LICENSE",
|
||||
"size": 1012,
|
||||
"sha256": "f3bc8715bad84b26396bb42d2abd11f919cf58163be158eab79ecbaabf84cdf2"
|
||||
},
|
||||
{
|
||||
"path": "package.json",
|
||||
"url": "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/9e04201d4557e729822fb57f62a316c3dea1d4a8/package.json",
|
||||
"size": 587,
|
||||
"sha256": "ffeb0e4cc17b4cb124408bd0068667b9a35e985971792978e40d4cd69c7ca8b7"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"url": "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/9e04201d4557e729822fb57f62a316c3dea1d4a8/README.md",
|
||||
"size": 1399,
|
||||
"sha256": "db5acca5eb4e2c9113c3408ed1ae7d17c76f653340a5d4e3f8a58d2bcdf50b7d"
|
||||
},
|
||||
{
|
||||
"path": "meta.json",
|
||||
"url": "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/9e04201d4557e729822fb57f62a316c3dea1d4a8/meta.json",
|
||||
"size": 2045073,
|
||||
"sha256": "ed9d7c7a5ca3ce7dee023fb6c1d62715dbec5b27503d268cbc10b9c8894ef3e8"
|
||||
},
|
||||
{
|
||||
"path": "tag.json",
|
||||
"url": "https://api.github.com/repos/Templarian/MaterialDesign-SVG/git/tags/5edde266e281d26a03dcfa89fb651183cbab0f2e",
|
||||
"size": 685,
|
||||
"sha256": "f02dc7fc1e862c344c66877828b1914543f0656db8544c05a1ed3b7d990b6d75"
|
||||
},
|
||||
{
|
||||
"path": "tree.json",
|
||||
"url": "https://api.github.com/repos/Templarian/MaterialDesign-SVG/git/trees/9e04201d4557e729822fb57f62a316c3dea1d4a8?recursive=1",
|
||||
"size": 1813728,
|
||||
"sha256": "b75710c2b9ee4d0ad3f79016e9767a85103f16df6cbbe5f0629efc3b470d1569"
|
||||
},
|
||||
{
|
||||
"path": "Apache-2.0.txt",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
"size": 11358,
|
||||
"sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"
|
||||
}
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@mdi/svg",
|
||||
"version": "7.4.47",
|
||||
"description": "Material Design Icons SVG",
|
||||
"scripts": {
|
||||
"verify": "node scripts/verify.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Templarian/MaterialDesign-SVG.git"
|
||||
},
|
||||
"keywords": [
|
||||
"material",
|
||||
"design",
|
||||
"icons",
|
||||
"mdi"
|
||||
],
|
||||
"author": "Austin Andrews",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Templarian/MaterialDesign/issues"
|
||||
},
|
||||
"homepage": "https://materialdesignicons.com/"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"node_id":"TA_kwDOBVEjNNoAKDVlZGRlMjY2ZTI4MWQyNmEwM2RjZmE4OWZiNjUxMTgzY2JhYjBmMmU","sha":"5edde266e281d26a03dcfa89fb651183cbab0f2e","url":"https://api.github.com/repos/Templarian/MaterialDesign-SVG/git/tags/5edde266e281d26a03dcfa89fb651183cbab0f2e","tagger":{"name":"Austin Andrews","email":"admin@templarian.com","date":"2023-12-26T23:33:26Z"},"object":{"sha":"9e04201d4557e729822fb57f62a316c3dea1d4a8","type":"commit","url":"https://api.github.com/repos/Templarian/MaterialDesign-SVG/git/commits/9e04201d4557e729822fb57f62a316c3dea1d4a8"},"tag":"v7.4.47","message":"v7.4.47\n","verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null,"verified_at":null}}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-usb" viewBox="0 0 24 24"><path d="M15,7V11H16V13H13V5H15L12,1L9,5H11V13H8V10.93C8.7,10.56 9.2,9.85 9.2,9C9.2,7.78 8.21,6.8 7,6.8C5.78,6.8 4.8,7.78 4.8,9C4.8,9.85 5.3,10.56 6,10.93V13A2,2 0 0,0 8,15H11V18.05C10.29,18.41 9.8,19.15 9.8,20A2.2,2.2 0 0,0 12,22.2A2.2,2.2 0 0,0 14.2,20C14.2,19.15 13.71,18.41 13,18.05V15H16A2,2 0 0,0 18,13V11H19V7H15Z" /></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-wifi-strength-4" viewBox="0 0 24 24"><path d="M12,3C7.79,3 3.7,4.41 0.38,7C4.41,12.06 7.89,16.37 12,21.5C16.08,16.42 20.24,11.24 23.65,7C20.32,4.41 16.22,3 12,3Z" /></svg>
|
||||
|
After Width: | Height: | Size: 219 B |
+19
-10
@@ -25,8 +25,8 @@ contents. Versions: ESP-IDF 5.5.0 / package 3.50500.0, Xtensa package
|
||||
TinyUSB 0.21.0~1, wolfSSL 5.8.2~1, wolfSSH 1.4.20.
|
||||
|
||||
The scoped inventory covers the six managed packages, the SDK/runtime groups
|
||||
in `docs/dependency_licenses.md`, two xterm packages, and the recorded icon
|
||||
summary. It retains configured-but-not-observed-linked groups too; inclusion
|
||||
in `docs/dependency_licenses.md`, two xterm packages, and the two verified icon
|
||||
SVG sources with pinned upstream metadata and full Apache-2.0 text. It retains configured-but-not-observed-linked groups too; inclusion
|
||||
is not a claim that each contributes to a release image. Full SDK/toolchain
|
||||
redistribution needs a broader review, including nested/test/tool licenses.
|
||||
No recursive license discovery is performed by the tool. Newly added files or
|
||||
@@ -68,13 +68,21 @@ license grant or independent upstream authentication is claimed by assembly.
|
||||
|
||||
## Icons and browser notices
|
||||
|
||||
The existing Pictogrammers summary is retained unchanged. Full Apache-2.0 text
|
||||
is supplied as `inputs/sdk/LICENSE`, the exact installed SDK copy; this is
|
||||
license text, **not proof of the individual icons' provenance or grant**.
|
||||
The project records Material Design Icons 7.4.47 and adapted USB/Wi-Fi masks.
|
||||
Exact two-icon origin/version/licensing and any applicable upstream NOTICE
|
||||
still need verification before distribution. No icon or web asset was changed
|
||||
or regenerated, and no upstream icon provenance was authenticated here.
|
||||
The existing Pictogrammers summary is retained unchanged. Both retained SVGs
|
||||
are byte-identical to official `@mdi/svg` 7.4.47 sources at commit
|
||||
`9e04201d4557e729822fb57f62a316c3dea1d4a8`. The bundle now includes those SVGs,
|
||||
unmodified upstream originals/license/package metadata, full author metadata,
|
||||
unsigned tag and complete tree evidence, separately fetched full Apache-2.0
|
||||
text, and `inputs/project/docs/icon_provenance.md`. Metadata credits USB to
|
||||
Google and Wi-Fi to Simran; the package declares Apache-2.0. No NOTICE-named
|
||||
path occurs in the pinned non-truncated distribution tree; none is invented.
|
||||
|
||||
See the included provenance record for exact coordinates and mockup transforms.
|
||||
The USB mockup path matches; its Wi-Fi path differs. Firmware masks remain
|
||||
manual adaptations with **no verified exact rasterization recipe**. Identity of
|
||||
the preferred SVG sources is resolved, not the historical/mechanical derivation
|
||||
of every adaptation. No existing icon, firmware or web asset was changed or
|
||||
regenerated. Full license text alone is not provenance or release clearance.
|
||||
|
||||
Both xterm MIT notices and `web_assets/SOURCES.md` are retained. This does not
|
||||
embed or serve notices in browser responses, authenticate preferred sources,
|
||||
@@ -105,5 +113,6 @@ proof of delivery.
|
||||
Installation Information, and validate recipient access and source offers.
|
||||
Do not publish keys, passwords, Wi-Fi secrets, tickets, verifiers, NVS/flash
|
||||
images, credential backups, or secret-bearing build configurations.
|
||||
5. Resolve wolfSSH packaging and icon provenance questions above. Do not use a
|
||||
5. Resolve wolfSSH packaging and the remaining manual-derivative provenance
|
||||
limits above. Do not use a
|
||||
successful hash check as legal sign-off or whole-Phase-9 acceptance.
|
||||
|
||||
+120
-3
@@ -199,13 +199,13 @@
|
||||
"size": 1012
|
||||
},
|
||||
{
|
||||
"output_sha256": "9875d806c9437b5dbdd7ba341beac4772abd8da8e1d14c1a6279fe802b43208b",
|
||||
"output_sha256": "ceb91c094ca7413af406cf7d8a12dc5d113053e982beb611f9b055bf83d9b881",
|
||||
"path": "third_party/release-notices/README.md",
|
||||
"purpose": "Scope, provenance, license choices and unresolved release gates",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "9875d806c9437b5dbdd7ba341beac4772abd8da8e1d14c1a6279fe802b43208b",
|
||||
"size": 6822
|
||||
"sha256": "ceb91c094ca7413af406cf7d8a12dc5d113053e982beb611f9b055bf83d9b881",
|
||||
"size": 7458
|
||||
},
|
||||
{
|
||||
"output_sha256": "8eb4021ed94ecea9bac60bda1a8be7bbc5ccbfe547196bb061dcbe29c01227de",
|
||||
@@ -599,6 +599,123 @@
|
||||
"root": "toolchain",
|
||||
"sha256": "422aa40293093fb54fc66e692a0d68fd0b24ed5602e5d1d33ad05ba3909057e9",
|
||||
"size": 63318
|
||||
},
|
||||
{
|
||||
"output_sha256": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/usb.svg",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
|
||||
"size": 403
|
||||
},
|
||||
{
|
||||
"output_sha256": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/wifi-strength-4.svg",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
|
||||
"size": 219
|
||||
},
|
||||
{
|
||||
"output_sha256": "f3bc8715bad84b26396bb42d2abd11f919cf58163be158eab79ecbaabf84cdf2",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/LICENSE",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "f3bc8715bad84b26396bb42d2abd11f919cf58163be158eab79ecbaabf84cdf2",
|
||||
"size": 1012
|
||||
},
|
||||
{
|
||||
"output_sha256": "ffeb0e4cc17b4cb124408bd0068667b9a35e985971792978e40d4cd69c7ca8b7",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/package.json",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "ffeb0e4cc17b4cb124408bd0068667b9a35e985971792978e40d4cd69c7ca8b7",
|
||||
"size": 587
|
||||
},
|
||||
{
|
||||
"output_sha256": "db5acca5eb4e2c9113c3408ed1ae7d17c76f653340a5d4e3f8a58d2bcdf50b7d",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/README.md",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "db5acca5eb4e2c9113c3408ed1ae7d17c76f653340a5d4e3f8a58d2bcdf50b7d",
|
||||
"size": 1399
|
||||
},
|
||||
{
|
||||
"output_sha256": "ed9d7c7a5ca3ce7dee023fb6c1d62715dbec5b27503d268cbc10b9c8894ef3e8",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/meta.json",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "ed9d7c7a5ca3ce7dee023fb6c1d62715dbec5b27503d268cbc10b9c8894ef3e8",
|
||||
"size": 2045073
|
||||
},
|
||||
{
|
||||
"output_sha256": "f02dc7fc1e862c344c66877828b1914543f0656db8544c05a1ed3b7d990b6d75",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/tag.json",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "f02dc7fc1e862c344c66877828b1914543f0656db8544c05a1ed3b7d990b6d75",
|
||||
"size": 685
|
||||
},
|
||||
{
|
||||
"output_sha256": "b75710c2b9ee4d0ad3f79016e9767a85103f16df6cbbe5f0629efc3b470d1569",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/tree.json",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "b75710c2b9ee4d0ad3f79016e9767a85103f16df6cbbe5f0629efc3b470d1569",
|
||||
"size": 1813728
|
||||
},
|
||||
{
|
||||
"output_sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/Apache-2.0.txt",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
|
||||
"size": 11358
|
||||
},
|
||||
{
|
||||
"output_sha256": "84408409a100b3eb5abd36c596a6539248d608b5c0cae463c60fd45506ff1eca",
|
||||
"path": "third_party/material-design-icons/upstream-7.4.47/downloads.json",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "84408409a100b3eb5abd36c596a6539248d608b5c0cae463c60fd45506ff1eca",
|
||||
"size": 2328
|
||||
},
|
||||
{
|
||||
"output_sha256": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
|
||||
"path": "third_party/material-design-icons/usb.svg",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "c9918e9a983fbd788378ca4c524e7a07a0d5eedcaeff73d19814e6f6ae221f22",
|
||||
"size": 403
|
||||
},
|
||||
{
|
||||
"output_sha256": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
|
||||
"path": "third_party/material-design-icons/wifi-strength-4.svg",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "89d14daf863076b0f73c76d913212875f2e9bcddaaf49c9e5d1825e0b2dc2d5f",
|
||||
"size": 219
|
||||
},
|
||||
{
|
||||
"output_sha256": "e8ad06e9c8b8e1256b8d829436c50f57de35ad9e666d54b3ea5d93deea54fd6c",
|
||||
"path": "docs/icon_provenance.md",
|
||||
"purpose": "Exact two-icon source/license/provenance evidence; manual-derivative limits in docs/icon_provenance.md",
|
||||
"range": null,
|
||||
"root": "project",
|
||||
"sha256": "e8ad06e9c8b8e1256b8d829436c50f57de35ad9e666d54b3ea5d93deea54fd6c",
|
||||
"size": 7263
|
||||
}
|
||||
],
|
||||
"schema": 1,
|
||||
|
||||
@@ -28,8 +28,9 @@ verbatim, including mixed grants and the wolfSSH package/header discrepancy.
|
||||
|
||||
This is NOT corresponding source, a complete SBOM, an archive of the SDK/tools,
|
||||
or proof of notice delivery to firmware/device/browser recipients. Radio-blob
|
||||
corresponding-source/System Library questions remain unresolved. Icon provenance,
|
||||
wolfSSH packaging clarification, bootloader/runtime attribution, source delivery
|
||||
corresponding-source/System Library questions remain unresolved. Exact icon SVG
|
||||
source evidence and manual-derivative limits are in inputs/project/docs/icon_provenance.md.
|
||||
WolfSSH packaging clarification, bootloader/runtime attribution, source delivery
|
||||
and Installation Information still require release review. No legal clearance.
|
||||
""".encode("utf-8")
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bounded Phase 9 host validation; build and local interop are explicit opt-ins."""
|
||||
import argparse
|
||||
from dataclasses import dataclass
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BUILD = Path('.pio/build/esp32-s3-devkitc-1-n16r8')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Command:
|
||||
name: str
|
||||
argv: tuple
|
||||
timeout: float
|
||||
required: tuple = ()
|
||||
|
||||
|
||||
def positive_seconds(value):
|
||||
seconds = float(value)
|
||||
if not math.isfinite(seconds) or not 0 < seconds <= 3600:
|
||||
raise argparse.ArgumentTypeError('timeout must be finite and in (0, 3600] seconds')
|
||||
return seconds
|
||||
|
||||
|
||||
def parser():
|
||||
cli = argparse.ArgumentParser(description=__doc__)
|
||||
cli.add_argument('--build', action='store_true', help='run pio run before host checks (may fetch dependencies)')
|
||||
cli.add_argument('--interop', action='store_true', help='enable ordering OpenSSH local Unix-socket matrix')
|
||||
cli.add_argument('--dry-run', action='store_true', help='print plan only; no execution or prerequisite validation')
|
||||
cli.add_argument('--fail-fast', action='store_true', help='stop after first failure; default collects failures')
|
||||
cli.add_argument('--timeout', type=positive_seconds, default=180, help='seconds per host command (default: 180, maximum: 3600)')
|
||||
cli.add_argument('--build-timeout', type=positive_seconds, default=600, help='seconds for optional build (default: 600, maximum: 3600)')
|
||||
return cli
|
||||
|
||||
|
||||
def plan(options):
|
||||
commands = []
|
||||
if options.build:
|
||||
commands.append(Command('build', ('pio', 'run'), options.build_timeout))
|
||||
|
||||
def suite(name, *args, required=(), label=None):
|
||||
path = Path('tests') / name / 'run.py'
|
||||
commands.append(Command(label or name, (sys.executable, '-B', str(path), *map(str, args)),
|
||||
options.timeout, (path, *required)))
|
||||
|
||||
header = BUILD / 'config/sdkconfig.h'
|
||||
database = BUILD / 'compile_commands.json'
|
||||
suite('security_build_policy', '--sdkconfig-header', header, required=(header,))
|
||||
for name in ('ssh_auth_policy', 'ssh_auth_transport', 'hidden_input', 'ssh_memory'):
|
||||
suite(name)
|
||||
suite('sdk_security_overrides', '--build-dir', BUILD, required=(database,))
|
||||
for name in ('wolfssh_auth_contract', 'ssh_protocol_policy', 'wolf_crypto_policy'):
|
||||
suite(name, '--compile-commands', database, required=(database,))
|
||||
suite('wolfssh_parser_contract')
|
||||
suite('wolfssh_order_contract', *(['--interop'] if options.interop else []))
|
||||
suite('release_notices')
|
||||
for name in ('ssh_management', 'admin_console_boundary', 'admin_ssh_policy',
|
||||
'web_admin_transport', 'web_admin_tickets', 'web_httpd_idle'):
|
||||
suite(name)
|
||||
# The current runner has no --admission branch: its default covers admission.
|
||||
for mode in ('base', 'admin', 'accounts', 'ssh', 'lifecycle'):
|
||||
suite('web_cookie_auth', *([] if mode == 'base' else ['--' + mode]),
|
||||
label='web_cookie_auth:' + mode)
|
||||
return commands
|
||||
|
||||
|
||||
def kill_group(process):
|
||||
try:
|
||||
os.killpg(process.pid, signal.SIGKILL)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
process.wait(timeout=5)
|
||||
|
||||
|
||||
def run_command(command, root, env):
|
||||
missing = [str(path) for path in command.required if not (root / path).is_file()]
|
||||
if missing:
|
||||
return 'PREREQ', 'missing ' + ', '.join(missing)
|
||||
try:
|
||||
# Inherit stdout/stderr: no persistent captures or environment dumps.
|
||||
process = subprocess.Popen(command.argv, cwd=root, env=env, stdin=subprocess.DEVNULL,
|
||||
start_new_session=True)
|
||||
except OSError as error:
|
||||
return 'PREREQ', f'cannot launch command (errno {error.errno})'
|
||||
try:
|
||||
code = process.wait(timeout=command.timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
kill_group(process)
|
||||
return 'TIMEOUT', f'exceeded {command.timeout:g}s; process group killed'
|
||||
except KeyboardInterrupt:
|
||||
kill_group(process)
|
||||
raise
|
||||
# Also retire any descendants left by a runner that exited early.
|
||||
try:
|
||||
os.killpg(process.pid, signal.SIGKILL)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
return ('PASS', 'exit 0') if code == 0 else ('FAIL', f'exit {code}')
|
||||
|
||||
|
||||
def execute(commands, root, *, dry_run=False, fail_fast=False):
|
||||
if os.name != 'posix':
|
||||
print('PREREQ: POSIX process groups required', flush=True)
|
||||
return 1
|
||||
env = os.environ.copy()
|
||||
env['CCACHE_DISABLE'] = '1'
|
||||
results = []
|
||||
stopped = False
|
||||
for command in commands:
|
||||
print(f'COMMAND {command.name} timeout={command.timeout:g}s argv={list(command.argv)!r}', flush=True)
|
||||
if dry_run:
|
||||
status, detail = 'PLAN', 'not executed; prerequisites not checked'
|
||||
elif stopped:
|
||||
status, detail = 'SKIP', 'earlier failure (fail-fast or failed build)'
|
||||
else:
|
||||
started = time.monotonic()
|
||||
try:
|
||||
status, detail = run_command(command, root, env)
|
||||
except KeyboardInterrupt:
|
||||
print(f'INTERRUPTED {command.name}; process group killed', flush=True)
|
||||
return 130
|
||||
detail += f' ({time.monotonic() - started:.1f}s)'
|
||||
if status != 'PASS' and (fail_fast or command.name == 'build'):
|
||||
stopped = True
|
||||
results.append((command.name, status, detail))
|
||||
print(f'{status} {command.name}: {detail}', flush=True)
|
||||
print('\nSUMMARY (command exits, not target validation):', flush=True)
|
||||
for name, status, detail in results:
|
||||
print(f'{status} {name}: {detail}', flush=True)
|
||||
return int(any(status not in ('PASS', 'PLAN') for _, status, _ in results))
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
options = parser().parse_args(argv)
|
||||
if not options.build:
|
||||
print('SKIP optional build: --build not requested; existing artifacts required', flush=True)
|
||||
if not options.interop:
|
||||
print('SKIP optional interop: --interop not requested', flush=True)
|
||||
return execute(plan(options), ROOT, dry_run=options.dry_run, fail_fast=options.fail_fast)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user