Commit Graph
31 Commits
Author SHA1 Message Date
kawa c00ac80e6c docs(11-01): complete UIE-02 plan — Add Printer page separation
- Create 11-01-SUMMARY.md with full execution record
- Update STATE.md: advance plan to 3, add decisions, update session
- Update ROADMAP.md: phase 11 progress to 1/4 summaries (In Progress)
2026-04-15 11:05:23 +02:00
kawa 3d2cdc4e77 feat(11-01): UIE-02 — dedicated Add Printer page at GET /printers/new
- Create imptune/templates/printers_new.html: full page extending base.html
  with plain <form action="/printers" method="post"> (no HTMX, browser follows 303)
  and inline driver upload sub-form
- Update imptune/templates/printers.html: remove inline Add Printer form,
  add <a href="/printers/new" role="button">Add Printer</a> link
- Add GET /printers/new route in imptune/api/pages.py (placed before /{printer_id})
- Change POST /printers to always return RedirectResponse(url='/printers', status_code=303)
- Update existing POST /printers tests to assert 303 + follow_redirects=False
- Update test_printer_form.py to check /printers/new instead of /printers (UIE-02 arch)
- UIE-02 tests GREEN; UIE-01/03 scaffolds remain RED (expected — not yet implemented)
2026-04-15 11:03:41 +02:00
kawa a02df7d292 test(11-01): add Wave 0 RED scaffolds for UIE-01/02/03
- Add 8 new test functions: printers_new_returns_200, create_printer_redirects,
  printers_library_no_form, patch_printer, patch_printer_not_found,
  client_detail_returns_200, client_detail_not_found, client_links_in_printer_list
- Add FIXME comments on existing POST /printers tests noting upcoming 303 change
- All new tests FAIL against current code (proper RED TDD state)
- Existing 13 tests remain GREEN
2026-04-15 11:00:55 +02:00
kawaandClaude Sonnet 4.6 036b048705 docs(phase-09): complete phase execution and human sign-off
All 3 UX defects fixed, 11/11 must-haves verified, human-approved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:52:43 +02:00
kawaandClaude Sonnet 4.6 74535ea089 fix(intunewin): compute HMAC over IV+ciphertext, not ciphertext alone
The reference implementation (svrooij/ContentPrep Zipper.cs DecryptFileAsync)
reads the first 32 bytes as the stored HMAC, then hashes the *remaining* bytes
— i.e. IV (16 bytes) || ciphertext — to verify integrity. ImpTune was computing
HMAC(mac_key, ciphertext) which omits the IV. Intune's server-side HMAC check
would therefore always fail, manifesting as the same silent symptom as the
Detection.xml bug: empty wizard fields, greyed OK button, no error banner.

The blob layout is unchanged: [HMAC(32)] + [IV(16)] + [ciphertext].
Only the hash input is corrected: iv + ciphertext instead of ciphertext.

The Mac field in Detection.xml is also updated accordingly (it stores the same
HMAC value that is prepended to the blob).

Tests updated: test_hmac_matches now verifies HMAC over blob[32:] (= IV+ciphertext),
which is exactly what the reference decryption algorithm verifies against.

All 114 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 12:08:43 +02:00
kawaandClaude Sonnet 4.6 77162466d5 fix(intunewin): align Detection.xml with IntuneWinAppUtil.exe reference format
Four structural defects in the generated Detection.xml caused Intune's upload
wizard to silently fail metadata parsing (empty form, OK button greyed):

1. Missing ToolVersion="1.8.6.0" XML attribute on ApplicationInfo — the wizard
   uses this to validate the package was produced by a compatible tool.
2. Spurious xmlns="http://schemas.microsoft.com/IntuneWin" namespace — changes
   element identity for Intune's XML parser (reference emits no namespace).
3. <?xml version="1.0" ?> declaration header — reference uses OmitXmlDeclaration=true.
4. Extra <MacAlgorithm> child element inside EncryptionInfo — not present in
   the reference FileEncryptionInfo model (svrooij/ContentPrep verified).

Fix: switched from toprettyxml() to tostring(xml_declaration=False)+indent(),
added ToolVersion attribute, removed xmlns and MacAlgorithm.
Tests updated to assert the corrected reference format; all 114 pass.

Root cause verified against svrooij/ContentPrep Packager.cs + ApplicationInfo.cs
(open-source C# reference implementation of IntuneWinAppUtil.exe).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 11:48:36 +02:00
kawaandClaude Opus 4.6 27ddc77ee1 fix(inf-parser): tolerate bare-line sections in real vendor INFs
Real INFs (e.g. Ricoh oemsetup.inf) include [SourceDisksFiles] entries
with bare filename lines (no '='), which strict configparser rejects
with ParsingError, surfacing as a 500 on /drivers/upload.

Pre-process the INF text to rewrite bare lines into synthetic
__bare_N = <line> entries before parsing, and filter those synthetic
keys out of DriverDesc extraction so they cannot leak into driver_names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:11:45 +02:00
kawa 322fc2050f test(09-02): add Playwright UX-02 port autofill test
- Create tests/e2e/test_port_autofill.py - headless chromium test for PRNT-03
- Verifies IP->port_name auto-derivation via Alpine @input handler at /printers
- Update 09-VALIDATION.md: mark UX-02 tasks green, cite test file as evidence
2026-04-13 10:57:07 +02:00
kawa 4e9bd9b1ab chore(09-02): add Playwright dev deps and e2e package scaffolding
- Add pytest-playwright and playwright to requirements-dev.txt
- Create tests/e2e/__init__.py package marker
- Create tests/e2e/conftest.py with session-scoped live_server fixture
2026-04-13 10:55:49 +02:00
kawa 72c6a98578 feat(09-01): add inline driver upload to printer form with OOB refresh
- Add id="printer-form-driver-select" to driver <select> (OOB swap target)
- Add sibling <form hx-post="/drivers/upload"> AFTER printer </form>,
  inside Alpine x-data div — avoids invalid nested form HTML
- Hidden <input name="caller" value="printer_form"> sentinel triggers OOB path
- Add hidden <div id="driver-list"> anchor for HTMX outerHTML swap target
- Create tests/test_printer_form.py with test_printer_form_has_inline_driver_upload
  asserting stable select id, caller sentinel, and non-nested upload form

Full non-e2e suite: 112 passed.
2026-04-13 10:51:46 +02:00
kawa d1de8395a3 test(09-01): add failing driver upload 500 regression + OOB contract tests
- Add _make_driver_zip_with_cat() helper for .inf + .cat fixture ZIPs
- Add _make_bom_driver_zip() helper for UTF-16 LE BOM encoded INF edge case
- Add test_upload_500_regression (parametrized: plain UTF-8 + BOM variant)
- Add test_upload_returns_oob_when_called_from_form (RED: handler lacks caller param)
- Add test_upload_oob_autoselects_new_driver (RED: no OOB fragment emitted)
- Add test_upload_no_oob_from_standalone_drivers_page

OOB tests fail: handler returns driver_list.html always regardless of caller field.
500 regression tests pass: existing synthetic ZIPs work fine against current handler.
2026-04-13 10:49:34 +02:00
kawa d3590017e9 test(09-03): add failing .ps1 route and detail-page link tests
- Create tests/test_script_download.py with 5 tests for .ps1 routes (install/uninstall/detect, 404, 422)
- Add test_detail_page_shows_script_links to TestCommandPreview in tests/test_packages.py
- All 6 new tests go RED (routes and template links do not exist yet)
2026-04-13 10:47:09 +02:00
kawaandClaude Opus 4.6 67a1cd66ec chore: complete v1.0 milestone
Archive v1.0 MVP: 7 phases, 13 plans, 27/27 requirements.

- Archive roadmap to milestones/v1.0-ROADMAP.md
- Archive requirements to milestones/v1.0-REQUIREMENTS.md
- Move milestone audit into milestones/
- Create MILESTONES.md with v1.0 entry
- Evolve PROJECT.md: move shipped requirements to Validated,
  update Context with stack/LOC, log Key Decisions with outcomes
- Collapse ROADMAP.md to one-line milestone summary
- Update STATE.md to shipped status
- Back-fill stale requirements-completed frontmatter on
  02-01, 04-01, 05-01, 06-01 SUMMARY.md files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 09:47:37 +02:00
kawa 8cf47f5c89 test(07-01): add failing tests for /packages route and dashboard data
- test_packages_returns_200 asserts /packages returns 200
- test_dashboard_shows_recent_printers verifies recent printers rendered
- test_dashboard_shows_recent_packages verifies only driver-assigned printers shown
2026-04-13 08:57:20 +02:00
kawa 2723cc8974 test(06-01): add failing test for icon inclusion in .intunewin export
- test_intunewin_includes_icon: uploads PNG icon, asserts icon.png in staged files
- test_intunewin_without_icon_succeeds: baseline no-icon export passes (already green)
2026-04-10 16:21:04 +02:00
kawa f96ea6fec9 feat(05-02): printer detail page with export buttons and command preview
- Update printer_detail() to pass install_cmd, uninstall_cmd, has_driver, has_icon to template
- Rewrite printer_detail.html with Intune Commands, Export, and Icon sections
- Show command strings with Alpine.js copy-to-clipboard buttons (install-cmd, uninstall-cmd)
- Show NinjaRMM ZIP and .intunewin download links when driver assigned
- Add HTMX icon upload form with #icon-status swap target
- Remove disabled placeholder Regenerate Package button
- Add TestCommandPreview class (4 tests) to test_packages.py
- All 94 tests pass
2026-04-10 15:06:45 +02:00
kawa f9e13ba96f feat(05-02): icon upload endpoint with validation
- Create imptune/api/icons.py with POST /printers/{printer_id}/icon
- Validate PNG format, 256x256 dimensions, 750KB max size
- Store icons SHA256-addressed under cfg.DATA_DIR/icons/
- Replace existing Icon record on re-upload
- Register icons.router in main.py with ICONS_DIR makedirs
- Patch cfg.ICONS_DIR in conftest.py for tests
- All 6 icon upload tests pass
2026-04-10 15:05:36 +02:00
kawa d8ce223b38 test(05-02): add failing tests for icon upload endpoint
- Add test_icon_upload.py with 6 TDD RED tests for PKG-04
- Add Pillow>=10.0 to requirements.txt
- Add ICONS_DIR to imptune/config.py
2026-04-10 15:04:38 +02:00
kawa a31c71e11f test(05-01): add failing tests for NinjaRMM ZIP and intunewin package export endpoints
- TestNinjaDownload: 5 tests covering ZIP response, install.ps1, driver files, 404, 422
- TestIntunewinDownload: 4 tests covering .intunewin response, structure, 404, 422
- Fixtures: driver_zip_bytes, setup_printer_with_driver, printer_no_driver
2026-04-10 15:04:11 +02:00
kawa b7b0d1b4a5 feat(04-02): add script download API endpoints and router registration
- imptune/api/scripts.py: GET /printers/{id}/scripts/{install,uninstall,detect}
- 404 for missing printer, 422 for missing driver/INF/driver_desc
- Content-Disposition attachment header on all responses
- imptune/main.py: register scripts.router
- 5 integration tests covering all endpoint cases (200/404/422)
2026-04-10 13:36:07 +02:00
kawa 0f213df92c test(04-02): add failing tests for render_uninstall and render_detect
- test_render_uninstall: asserts Remove-Printer/Driver/Port order + SilentlyContinue x3
- test_render_detect: asserts Get-Printer, Write-Output, exit 0, exit 1
2026-04-10 13:34:22 +02:00
kawa b4f2c64161 test(04-01): add failing tests for script_generator RED phase
- test_render_install_returns_string: basic smoke test
- test_render_install_contains_pnputil: SCRPT-01 pnputil + Add-PrinterDriver
- test_render_install_print_config: SCRPT-01 duplex mode translation
- test_render_install_wow64_guard: SCRPT-05 WOW64 relaunch guard
- test_render_install_uac_guard: SCRPT-04 SYSTEM vs user + UAC elevation
- test_render_install_idempotency: SCRPT-01 idempotency checks
- test_render_install_booleans: SCRPT-01 boolean rendering
2026-04-10 13:31:00 +02:00
kawa 6e7892e862 test(03-02): add failing tests for printer detail page
- test_printer_detail_shows_driver: asserts driver name appears on detail page
- test_printer_detail_not_found: asserts 404 for missing printer ID
- test_printer_detail_no_driver: asserts graceful display when driver is None
2026-04-10 13:04:01 +02:00
kawa 356c2ee690 feat(03-01): implement printer and client CRUD with HTMX/Alpine.js UI
- imptune/api/printers.py: POST /printers (all form fields, checkbox->bool,
  FK resolution), DELETE /printers/{id}, grouped list renderer with LEFT JOIN
- imptune/api/clients.py: POST /clients with duplicate-name handling
- imptune/api/pages.py: GET /printers and GET /clients page routes
- imptune/main.py: register printers + clients routers; close db on shutdown
- imptune/db/database.py: close existing connection before re-init (test isolation)
- templates: printers.html, clients.html, printer_form.html (Alpine.js port
  auto-derivation), printer_list.html (grouped by client), client_list.html
- tests/conftest.py: close test-thread db connection in fixture teardown
- tests/test_printer_crud.py: updated to use list(select().where()) for DB
  queries (avoids Peewee thread-local cursor caching across test boundaries)

Auto-fix [Rule 1 - Bug]: DB test isolation — Peewee thread-local connections
persisted across tests causing stale DB reads; fixed via conftest teardown and
lifespan db.close() on shutdown.
2026-04-10 12:56:09 +02:00
kawa 9bc26e377b test(03-01): add failing integration tests for printer and client CRUD
- test_create_printer_persisted: POST /printers + GET /printers
- test_create_printer_duplex/color_mode/paper_size/collate: field persistence
- test_create_client: POST /clients + GET /clients
- test_printer_grouped_by_client: client group heading in list
- test_create_printer_missing_name/invalid_ip: validation 400s
- test_delete_printer: DELETE /printers/{id} removes record
2026-04-10 12:50:00 +02:00
kawa c648fc5793 feat(02-02): add driver upload endpoint with INF parsing and dedup
- POST /drivers/upload: validates ZIP, parses INF, persists via DriverStore + Peewee
- SHA256 dedup: get_or_create prevents duplicate Driver records
- GET /drivers route added to pages.py with driver_data context
- drivers.router registered in main.py
- drivers.html template with HTMX upload form
- partials/driver_list.html HTMX target with select dropdown and unused-file notice
- Fixed conftest client fixture to use context manager (triggers lifespan/init_db)
- [Rule 3] conftest: TestClient context manager required for lifespan trigger
- [Rule 1] drivers.py: dynamic DRIVERS_DIR read so monkeypatch works in tests
2026-04-10 12:02:00 +02:00
kawa 8ecfbf25a7 test(02-02): add failing integration tests for driver upload 2026-04-10 11:59:30 +02:00
kawa 290106d573 test(02-01): add failing tests for INF parser
- 11 test functions covering all DRV-02 and DRV-05 behaviors
- _detect_encoding tests for UTF-16 LE, UTF-16 BE, UTF-8 BOM, ANSI/cp1252
- parse_inf tests: literal DriverDesc, %TOKEN% resolution, UTF-16 fixture
- Multi-model deduplication, architecture detection (x64/arm64/x86/mixed)
- .cat file detection, unused files detection, empty models section
- INF fixtures: sample.inf, sample_utf16.inf (UTF-16 LE BOM), sample_multi_model.inf
- imptune/services/__init__.py package marker
2026-04-10 11:55:41 +02:00
kawa dea41483b5 feat(01-02): add Peewee models, database init, and driver storage
- imptune/db/database.py: deferred SqliteDatabase with WAL + foreign_keys pragmas and init_db()
- imptune/db/models.py: full schema (Client, Driver, Printer, Icon) for phases 1-5
- imptune/storage/driver_store.py: SHA256 content-addressed DriverStore with dedup
- tests/test_db.py: 7 TDD tests covering all db and storage behaviors
2026-04-10 11:31:39 +02:00
kawa 34c7cb30f1 feat(01-01): add test scaffold, health and static asset tests, fix deprecated APIs
- requirements-dev.txt with pytest and httpx
- tests/conftest.py: client and tmp_data_dir fixtures
- tests/test_health.py: GET /health returns 200 with {"status": "ok"}
- tests/test_static.py: no CDN URLs in templates, dashboard returns 200
- Fix imptune/api/pages.py: use request= kwarg in TemplateResponse (Starlette compat)
- Fix imptune/main.py: replace deprecated on_event with asynccontextmanager lifespan
2026-04-10 11:26:48 +02:00
kawa 4d455e7b19 test(01-03): add failing tests for .intunewin byte-level format
- 14 tests covering outer ZIP structure, Detection.xml schema, key sizes
- Cryptographic roundtrip: HMAC-SHA256, AES-256-CBC decryption, file digest
- IV size assertion: must be 16 bytes (critical correction from RESEARCH.md)
- Tests serve as format specification for build_intunewin() implementation
2026-04-10 11:24:45 +02:00