Some checks failed
Blocked since Phase 0 because cargo-deny resolves from the lockfile, and unblocked once Cargo.lock was committed in 0.2. I predicted this would surface a large backlog. It did: 5 advisories, 6 licence rejections, 126 git-source errors and 20 unlicensed crates. The triage is the work here, not the config. ONE ACTUAL VULNERABILITY. RUSTSEC-2026-0187: unbounded recursion parsing nested PDF objects in lopdf 0.31, reached via printpdf 0.7. A ~21 KB crafted file aborts the process with SIGABRT, and because it is a stack overflow rather than a panic, catch_unwind cannot contain it. Not exploitable here, and I checked rather than assumed. The advisory is specifically about Document::load*, the parsing entry points. This crate never parses a PDF -- arch_pdf.rs only writes them through printpdf's drawing API, there is no lopdf import anywhere in the workspace, and there is no PDF read path of any kind. There is also no fix in range: printpdf 0.7 pins lopdf 0.31 and `cargo update -p lopdf` moves nothing. Clearing it means printpdf 0.8+, a breaking change across arch_pdf.rs. So it is ignored with the reasoning written down AND with the condition that invalidates it: the moment anything in this crate reads a PDF -- an import feature, a thumbnailer, a preview pane -- this becomes a live DoS and the ignore must go. An ignore without its expiry condition is how real vulnerabilities get inherited. The other four advisories are unmaintained/unsound transitive crates (proc-macro-error, ttf-parser, atomic-polyfill, glib VariantStrIter), none with a safe upgrade, all arriving through the GUI/platform stack. Listed individually rather than disabling the unmaintained class, so a new one still fails. Six licence rejections were permissive licences simply absent from the allow list. MPL-2.0 (option-ext) is weak copyleft, so it is granted to that one crate rather than added globally -- MPL reciprocity is per-file and only bites if the crate is vendored and edited. The 126 git-source errors and 20 unlicensed crates are artifacts, not findings. Every git dependency is pinned to a full rev, which is stronger than a version range and already enforced by its own CI step; the unlicensed crates are first-party and vendored forks with no license field. Both handled WITHOUT allow-ing the class: sources are governed by the rev-pinning step, and the 20 crates are clarified by name, so a genuinely new unlicensed dependency still fails. Blanket-disabling would have hidden exactly the case worth catching. Separate file from deny.toml on purpose. Merging them would mean loosening the payment rules to fit a Makepad + Robius graph, and the payment policy is the one worth keeping tight. Verified both pay crates still pass unchanged. Negative-tested in both directions: removing the lopdf ignore fails the gate, and removing MIT from the allow list produces 79 rejections. A gate that cannot fail is worse than no gate. 622 lib + 154 integration, 0 failed.
264 lines
12 KiB
Markdown
264 lines
12 KiB
Markdown
# Phase 0 — "Make the crate verifiable" — status
|
||
|
||
Phase 0 of the plan in `CAD_ASSESSMENT_AND_PLAN.md`. Its purpose is to make
|
||
changes to the CAD module *provable* rather than plausible. Before this
|
||
work, the crate could not be resolved, built, linted, or tested at all.
|
||
|
||
**Result: `nigig-build` now compiles and its test suite runs.**
|
||
|
||
- CAD module: 0 compile errors (was 1).
|
||
- Whole crate: 0 compile errors (was 44).
|
||
- Test suite: **634 passed, 17 failed, 7 ignored** — the first baseline
|
||
this crate has ever had. See `TEST_BASELINE.md`.
|
||
|
||
The 44 sibling-module errors documented below as "deferred" were
|
||
subsequently fixed; the section is kept as a record of what was wrong and
|
||
why. CI now gates on the full crate.
|
||
|
||
---
|
||
|
||
## Completed
|
||
|
||
### 0.1 — All git dependencies pinned to a rev
|
||
|
||
33 manifests contained `branch = "..."` (or nothing at all) on git
|
||
dependencies. Every build therefore re-resolved to whatever the branch head
|
||
was — unreproducible by construction, and a direct code-execution path into
|
||
the build if the branch is force-pushed.
|
||
|
||
| Repository | Was | Now (`rev`) |
|
||
|---|---|---|
|
||
| `gitdab.com/andodeki/makepad` | `branch = "portallist_flow_adaptive_view"` | `16a4d04cbb21c82df323f4fe02133b9b260ed36f` |
|
||
| `github.com/project-robius/robius` | *(unpinned — no branch or rev)* | `b766e62b0600f5d2ee21cc6995648346fc277bd8` |
|
||
|
||
Both revs are the branch/HEAD commits as resolved on 2026-07-27, so this
|
||
pins current behaviour rather than changing it.
|
||
|
||
### 0.2 — `Cargo.lock` committed
|
||
|
||
`Cargo.lock` now exists at the workspace root: **540 packages, 488 locked**.
|
||
`cargo metadata --locked` is a no-op against it.
|
||
|
||
Producing it required fixing three genuine resolution failures (below) —
|
||
the workspace had never resolved.
|
||
|
||
### 0.3 — CI workflow
|
||
|
||
`.forgejo/workflows/nigig-build.yml`. Two jobs that are green today:
|
||
|
||
- **`supply-chain`** — every live git dependency is pinned to a rev
|
||
(comment lines excluded; verified with a negative test); `Cargo.lock` is
|
||
committed and unchanged by resolution; no whitespace errors.
|
||
- **`cad-module`** — installs the native GL/Wayland stack, checks
|
||
formatting.
|
||
|
||
- **`full-crate-check`** — `cargo check` on the whole lib, plus
|
||
`cargo test` with a **regression gate**: the build fails if failures rise
|
||
above the recorded baseline of 17 or passes fall below 634. This was
|
||
commented out when first written (the crate had 44 compile errors); it is
|
||
now live and required. Verified locally in both directions — it passes on
|
||
the current tree and correctly rejects a simulated 18th failure.
|
||
|
||
### 0.5 — Stale documentation removed
|
||
|
||
Deleted, having been shown to contradict the code:
|
||
|
||
| File | Why |
|
||
|---|---|
|
||
| `cad/PROGRESS.md` | Claimed parallel mesh generation and the Send/Sync audit were "⏸ Deferred". Both shipped. Credited the command pattern to the `Legacy*` system, which is dead. |
|
||
| `cad/README.md` | A patch-distribution note ("drop these files into…"). Told the reader to add `rayon` to `Cargo.toml` — already present. Claimed `build_glb()` is parallel by default — it is sequential below 32 nodes. |
|
||
| `cad/SPLIT_PLAN.md` | A line-number map of a version of `mod.rs` that no longer exists. |
|
||
| `cad/CHANGES.md` | A changelog for a superseded patch drop. |
|
||
| `workspace/Next` | A raw AI chat transcript about *spreadsheet* features, committed into the tree. |
|
||
|
||
Replaced by one file: **`cad/ARCHITECTURE.md`**, which describes the system
|
||
as it is — data flow, the 24 files, the load-bearing invariants (including
|
||
the ones that are currently *violated*, flagged as such), and the two
|
||
independent meshing pipelines.
|
||
|
||
### 0.6 — Benchmarks no longer assert on wall-clock time
|
||
|
||
All six `bench_*` functions in `profile_benchmarks.rs` are now
|
||
`#[ignore]`-d, so `cargo test` no longer runs timing-sensitive code.
|
||
|
||
Removed `assert!(warm < cold)` from `bench_scene_cache_hit_vs_rebuild`: it
|
||
compared two single un-warmed samples and would flake under CI load.
|
||
|
||
---
|
||
|
||
## Resolution failures fixed along the way
|
||
|
||
These blocked `Cargo.lock` entirely. None were introduced by this work.
|
||
|
||
**1. `nigig-rider` requested a `makepad-widgets` feature that does not exist.**
|
||
|
||
```
|
||
package `nigig-rider` depends on `makepad-widgets` with feature `map_style`
|
||
but `makepad-widgets` does not have that feature
|
||
```
|
||
|
||
Available: `voice`, `maps`, `gltf`, `csg`, `test`, `pdf`, `cef`, `serde`.
|
||
Removed `map_style`, kept `maps`.
|
||
|
||
**2. Two `rusqlite` versions both linking `sqlite3`.**
|
||
|
||
```
|
||
libsqlite3-sys v0.32 (robius-sms) vs v0.28 (matrix_client → rusqlite 0.31)
|
||
note: only one package in the dependency graph may specify the same links value
|
||
```
|
||
|
||
This one is `nigig-build`'s own problem: it depends on `robius-sms`
|
||
(rusqlite 0.32) *and* `nigig-core` → `matrix_client` (rusqlite 0.31).
|
||
`nigig-pay-storage` and `robius-sms` already standardise on 0.32, so
|
||
`matrix_client` was the lone straggler — bumped it to 0.32.
|
||
|
||
**3. `spreadsheet-ui` did not compile** (4 × `E0308`).
|
||
|
||
`grid.rs` had four `cells.get(&(r, c))` calls missed during a
|
||
`(row, col)` → `CellId::new(row, col)` migration; the rest of the file was
|
||
already migrated. Fixed to match.
|
||
|
||
---
|
||
|
||
## Fixed in the CAD module
|
||
|
||
**`section_shape.rs` — `E0603`, private import re-export.**
|
||
|
||
```rust
|
||
use super::math::DVec2; // math.rs imports DVec2 privately
|
||
```
|
||
|
||
Changed to `use makepad_widgets::DVec2;`. Also dropped a now-unused
|
||
`std::f64::consts::PI` import. This was the **only** compile error in the
|
||
entire CAD module.
|
||
|
||
---
|
||
|
||
## Sibling modules — 44 errors, now FIXED
|
||
|
||
**Status: resolved.** Recorded here because the diagnosis is the useful
|
||
part. None of these were CAD code; they blocked `cargo test` because the
|
||
CAD workspace links against them (`cad/mod.rs:2055` calls
|
||
`cost_estimator::script_mod(vm)`).
|
||
|
||
| Module | Errors | Nature |
|
||
|---|--:|---|
|
||
| `doc/crdt_widget.rs` | 8 | `CrdtDocEditor` doesn't implement `WidgetNode`; missing `script_new`/`script_component`; 2 × "Unexpected field form" (a derive-macro rejection). Looks like a half-finished port to a newer Makepad widget API. |
|
||
| `cost_estimator/services.rs` | 8 | 6 × `E0308`; `Room` type not in scope. |
|
||
| `cost_estimator/data_raw.rs` | 8 | 8 × `E0308`. |
|
||
| `doc/widgets/doc_widget.rs` | 6 | Missing `CrdtDocument::to_json`/`from_json`; a borrow conflict; `return;` in a non-`()` function. |
|
||
| `cost_estimator/screen_actions.rs` | 5 | 4 × `E0308`; `&SizeCategory` used as a string pattern. |
|
||
| `doc/projection_renderer.rs` | 4 | 4 × `E0382` use-after-move on font handles. |
|
||
| `cost_estimator/room_controller.rs` | 2 | `String: Borrow<RoomTypeName>` unsatisfied. |
|
||
| `cost_estimator/ce_preview.rs` | 1 | `E0308`. |
|
||
| `cost_estimator/screen_events.rs` | 1 | `set_header_metrics` is private. |
|
||
|
||
Partially addressed: `screen_actions.rs` had 10, now 5 — `read_modal_f64`
|
||
and `read_modal_u32` took `LiveId` but every call site passes `ids!(...)`,
|
||
which is `&[LiveId; 1]`. Signatures corrected to `&[LiveId]`, matching the
|
||
`text_input(cx, ids!(…))` convention used throughout.
|
||
|
||
### How they were fixed
|
||
|
||
Three root causes, not 44 independent bugs:
|
||
|
||
1. **An unfinished `String` -> newtype migration in `cost_estimator/`.**
|
||
`Room.room_type`/`size_category` had become `RoomTypeName`/
|
||
`SizeCategory`, but the JSON parsers, the dimension-lookup map, the UI
|
||
DTO boundary and several tests still used `String`. Fixed by converting
|
||
at the boundary (`.into()` inbound, `.as_str().to_string()` outbound)
|
||
and keying the lookup maps on the newtypes.
|
||
|
||
2. **A derive-macro limitation in `doc/crdt_widget.rs`.** The
|
||
`Script`/`Widget` derives reject a fully-qualified path type in a
|
||
`#[rust]` field, emitting only "Unexpected field form" with no location.
|
||
Found by bisection. `use doc_engine::controller::DocumentController;`
|
||
plus a bare type name fixes it; a comment now records this so it is not
|
||
rediscovered.
|
||
|
||
3. **Borrow-scope and copy-paste defects in `doc/`.** Four `&mut DrawText`
|
||
moves inside a loop (needed reborrows); two `&self` calls made while a
|
||
`&mut` borrow was live (hoisted); and one block in
|
||
`delete_forward_command` that had been copied from the insert-text path
|
||
and still referenced its `input` variable, with `return;` in a `-> bool`
|
||
function. Forward-delete now replaces the selection with `String::new()`.
|
||
|
||
Also implemented `CrdtDocument::to_json`/`from_json` (serde derives on the
|
||
CRDT wire types) — these were called but had never been written.
|
||
|
||
### Test-build fixes
|
||
|
||
The lib compiled before the tests did; `cargo test` surfaced a further 77
|
||
errors in test code, including a stray `doc/tests.rs` whose first 66 lines
|
||
were a stale duplicate of `layout/layout_engine.rs` (its `super::` imports
|
||
could not resolve from that path). Removed the duplicate, kept the 11 real
|
||
tests. Two `cost_estimator` tests asserted `assert_ne!` between distinct
|
||
newtypes — a comparison the type system now rejects at compile time, which
|
||
is a stronger guarantee than the runtime assertion; rewritten to compare
|
||
`as_str()` and renamed accordingly.
|
||
|
||
---
|
||
|
||
## Build environment
|
||
|
||
The toolchain is not part of the workspace snapshot; re-create with:
|
||
|
||
```bash
|
||
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.97.1
|
||
rustup component add clippy rustfmt
|
||
|
||
sudo apt-get install -y pkg-config libwayland-dev libxcursor-dev \
|
||
libxrandr-dev libxi-dev libx11-dev libgl1-mesa-dev libasound2-dev \
|
||
libpolkit-gobject-1-dev libpolkit-agent-1-dev libglib2.0-dev \
|
||
libssl-dev libsqlite3-dev libudev-dev
|
||
```
|
||
|
||
Toolchain 1.97.1 matches `rust-toolchain.toml`.
|
||
|
||
---
|
||
|
||
## Not done
|
||
|
||
**0.4 — extend `cargo-deny` to `nigig-build`. DONE** (later; see
|
||
`deny-nigig-build.toml`).
|
||
|
||
The backlog I predicted was real: 5 advisories, 6 licence rejections, 126
|
||
git-source errors and 20 unlicensed first-party crates. The triage:
|
||
|
||
- **1 actual vulnerability.** RUSTSEC-2026-0187, unbounded recursion in
|
||
`lopdf` — a ~21 KB crafted PDF aborts the process with `SIGABRT`, and
|
||
because it is a stack overflow rather than a panic, `catch_unwind`
|
||
cannot contain it. **Not exploitable here**, verified rather than
|
||
assumed: the advisory covers `Document::load*`, and this crate never
|
||
parses a PDF. `arch_pdf.rs` only writes them, there is no `lopdf`
|
||
import anywhere in the workspace, and no PDF read path exists. No fix
|
||
is available in range either — `printpdf 0.7` pins `lopdf 0.31` and
|
||
`cargo update -p lopdf` moves nothing. Ignored with the reasoning and,
|
||
importantly, the condition that invalidates it: **the moment anything
|
||
here reads a PDF, this becomes a live DoS.**
|
||
- 4 unmaintained/unsound advisories, all transitive through the GUI and
|
||
platform stack, none with a safe upgrade. Listed individually so a new
|
||
one still fails.
|
||
- 6 licence rejections, all permissive licences simply missing from the
|
||
allow list. `MPL-2.0` (`option-ext`) is weak copyleft and is granted to
|
||
that one crate rather than added globally.
|
||
- The 126 git-source errors and 20 unlicensed crates are artifacts, not
|
||
findings — rev-pinned git dependencies and first-party manifests with
|
||
no `license` field. Handled without `allow`-ing the whole class, so a
|
||
genuinely new unlicensed dependency still fails.
|
||
|
||
Both directions verified: removing the `lopdf` ignore fails the gate, and
|
||
removing `MIT` from the allow list produces 79 rejections.
|
||
|
||
---
|
||
|
||
## Next
|
||
|
||
Phase 0's gate is met for the CAD module: it compiles, resolution is
|
||
pinned and reproducible, CI enforces both, and the documentation no longer
|
||
lies. Phase 1 (correctness) can begin.
|
||
|
||
Recommended first item remains **1.1, the rotation-unit inconsistency** —
|
||
it silently corrupts every export of a rotated part, and `ARCHITECTURE.md`
|
||
now carries a warning against writing new code that reads
|
||
`rotation_euler_xyz` until it is resolved.
|