Files
PSProxmoxVE/docs/review/REVIEW_REPORT.md
T
Clint Branham b4754c72a0 docs: update README, CHANGELOG, SECURITY.md, and review artifacts
- README: move Set-PveStorage to Storage section, Set-PveApiToken to
  Access section (were miscategorized under "Access — Groups & Domains")
- CHANGELOG: add Changed section with 13 architectural improvements,
  expand Fixed section with all ConfirmImpact and credential fixes
- SECURITY.md: add SecureString detail, Input Validation section,
  Dependabot mention, SkipCertificateCheck warning note
- CLAUDE.md: update review system reference to scan-7
- findings.json: F050 resolved (URL encoding), F031 resolved (credentials)
- REVIEW_REPORT.md: scan-7 report

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:41:19 -05:00

309 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PSProxmoxVE Module Review Report — Scan 7
```
Scan date: 2026-03-23
Prior report date: 2026-03-23 (scan-6)
PVE API spec date: 2026-03-21T15:04:50.641Z
PVE API spec SHA256: 4af79be30166209a4714b771f65e1e9540c5b738f414ff30c98454402e29d030
PVE version hint: N/A (not set in spec)
Total API endpoints: 646
Findings DB: docs/review/findings.json (F001F083)
Open findings: 12 (before scan) → 11 open + 1 regressed (after scan)
New this scan: 0 Resolved this scan: 1 (F082) Regressed: 1 (F050)
Last CI run: integration-tests.yml | success | 2026-03-23T21:42:02Z | https://github.com/goodolclint/PSProxmoxVE/actions/runs/23461565300
```
## Executive Summary
- **Delta**: 1 resolved (F082) | 0 new | 1 regressed (F050) | 11 open + 1 regressed
- **API drift**: 0 breaking changes in implemented endpoints | 42 PVE 9.0 endpoints unimplemented (F061)
- **CI**: Last integration run: **PASSED** | 0 test failures (both PVE 8 and PVE 9)
- **Code quality**: 11 of 12 DECISIONS.md entries upheld — **1 regression** (D003: URL encoding in 5 service files)
- Module has 169 cmdlets, ~170 markdown docs, netstandard2.0 targeting, 1,749 total tests (374 xUnit + 1,268 Pester + 107 integration)
- PSGallery manifest complete except IconUri (F021)
- All community/repo standards pass (issue templates, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, CODEOWNERS, etc.)
---
## Phase 1 — Repository Inventory & Structure
### Project Layout
| Item | Present | Path/Notes |
|---|---|---|
| Solution file | Yes | PSProxmoxVE.sln |
| Cmdlet project | Yes | src/PSProxmoxVE/ (netstandard2.0) |
| Core library | Yes | src/PSProxmoxVE.Core/ (netstandard2.0) |
| xUnit tests | Yes | tests/PSProxmoxVE.Core.Tests/ (net10.0;net48) |
| Pester tests | Yes | tests/PSProxmoxVE.Tests/ |
| Module manifest | Yes | src/PSProxmoxVE/PSProxmoxVE.psd1 |
| MAML help | Yes | src/PSProxmoxVE/PSProxmoxVE.dll-Help.xml |
| Format file | Yes | src/PSProxmoxVE/PSProxmoxVE.format.ps1xml |
| README.md | Yes | With badges (Build, Unit Tests, License, PSGallery) |
| CHANGELOG.md | Yes | Keep a Changelog format, 0.1.0-preview entry |
| CONTRIBUTING.md | Yes | Dev setup, coding standards, PR process |
| LICENSE | Yes | MIT |
| CODE_OF_CONDUCT.md | Yes | Contributor Covenant 2.1 |
| SECURITY.md | Yes | Vulnerability disclosure policy |
| DECISIONS.md | Yes | 12 active decisions (D001D012) |
| .editorconfig | Yes | Root config with per-filetype overrides |
| .gitignore | Yes | Comprehensive |
| .gitattributes | Yes | LF enforcement, binary handling, diff drivers |
| CODEOWNERS | Yes | `* @goodolclint` |
| dependabot.yml | Yes | Automated dependency updates |
| Issue templates | Yes | Bug report + feature request (YAML forms) + config.yml |
| PR template | Yes | Summary, type, checklist, API endpoints, testing |
| CI workflows | Yes | build.yml, unit-tests.yml, integration-tests.yml, publish.yml |
| Review system | Yes | docs/review/findings.json, docs/review/REVIEW_REPORT.md |
| Cmdlet docs | Yes | ~170 markdown files in docs/cmdlets/ |
### Missing Items
| Finding ID | Item | Notes |
|---|---|---|
| F021 | IconUri in manifest PSData | PSGallery listings display placeholder without it |
---
## Phase 2 — PVE API Coverage Audit
### Coverage by Functional Area
| Area | Spec Endpoints | Covered | % | Notes |
|------|---------------|---------|---|-------|
| VMs (qemu) | 97 | ~30 | 31% | Core CRUD, lifecycle, guest agent, disk ops, config |
| Containers (lxc) | 62 | ~18 | 29% | CRUD, lifecycle, snapshots, config, interfaces |
| Nodes | 77 | ~10 | 13% | List, status, config, DNS, startall/stopall |
| Firewall | 40 | ~22 | 55% | Rules, groups, aliases, IP sets, options, refs |
| Cluster | 79 | ~3 | 4% | Resources and status only |
| SDN | 60 | ~16 | 27% | Zones, VNets, subnets, IPAM, DNS, controllers |
| Storage | 24 | ~14 | 58% | CRUD, content, upload, download, allocate |
| Backup | 6 | 6 | 100% | Full coverage |
| Access/Users | 45 | ~22 | 49% | Users, groups, roles, domains, tokens, ACL, permissions |
| Tasks | 5 | ~4 | 80% | List, status, log, stop |
| Pools | 7 | ~5 | 71% | CRUD + list |
| HA | 21 | 0 | 0% | F053 — 🆕 Rules subsystem new in PVE 9.0 |
| Ceph | 40 | 0 | 0% | F054 |
| Notifications | 25+ | 0 | 0% | F068 — New in PVE 8.1+ |
| ACME/Certs | 23 | 0 | 0% | F069 |
| Disks | 18 | 0 | 0% | F067 |
| Replication | 5 | 0 | 0% | |
| Services | 7 | 0 | 0% | |
| **Total** | **646** | **~120** | **~18.6%** | |
### API Drift Table
No breaking changes detected in endpoints the module currently implements. All changes are additive (new optional parameters).
| Finding ID | Area | Change | PVE Version | Risk |
|---|---|---|---|---|
| F061 | HA rules | New `/cluster/ha/rules` subsystem | 9.0 | Additive |
| F061 | Bulk actions | New `/cluster/bulk-action` | 9.0 | Additive |
| F061 | SDN fabrics | New `/cluster/sdn/fabrics` | 9.0 | Additive |
| — | Various | 77 additive parameter changes across implemented endpoints | 8.x9.x | Additive |
### High-Value Uncovered Endpoints
**Priority 1 — Commonly needed:**
- `GET /access/acl` — List ACLs (only PUT covered)
- `GET /cluster/nextid` — Get next free VM ID
- `GET /cluster/ha/resources` — HA resource management
- VNC/SPICE console proxy endpoints
- `GET /nodes/{node}/subscription` — Subscription status
**Priority 2 — Frequently useful:**
- `GET /nodes/{node}/syslog` / `journal` — Log access
- `GET /nodes/{node}/hardware` — PCI passthrough info
- `GET /nodes/{node}/scan/*` — Storage discovery
- `POST /access/domains/{realm}/sync` — LDAP/AD sync
- APT package management
---
## Phase 3 — Code Quality & Best Practices
### DECISIONS.md Compliance
| Decision | Status | Notes |
|---|---|---|
| D001 — TaskService.WaitForTask | ✅ Compliant | No inline polling loops in cmdlets |
| D002 — SecureString passwords | ✅ Compliant | All 6 password params use SecureString |
| D003 — URL encoding | ❌ **REGRESSED (F050)** | 20 instances in 5 service files |
| D004 — No bare catch blocks | ✅ Compliant | All catches are specific or filtered |
| D005 — OutputType on all cmdlets | ✅ Compliant | All 169 cmdlets have OutputType |
| D006 — ConfirmImpact.High | ✅ Compliant | All destructive cmdlets confirmed |
| D007 — Sealed cmdlet classes | ✅ Compliant | All 169 cmdlets sealed |
| D008 — Newtonsoft.Json only | ✅ Compliant | No System.Text.Json attributes |
| D009 — Framework targets | ✅ Compliant | netstandard2.0 / net10.0;net48 |
| D010 — VmId ValidateRange | ✅ Compliant | All VmId params validated |
| D011 — Verb class constants | ✅ Compliant | No string literals |
| D012 — Magic string constants | ✅ Compliant | Auth headers extracted |
### D003 Regression Detail (F050)
20 instances of missing `Uri.EscapeDataString()` across 5 service files:
| File | Methods Affected | Unescaped Params |
|---|---|---|
| NodeService.cs | 7 (GetNodeStatus, GetNodeConfig, SetNodeConfig, GetDns, SetDns, StartAll, StopAll) | `node` |
| NetworkService.cs | 5 node-network (GetNetworks, CreateNetwork, SetNetwork, RemoveNetwork, ApplyNetworkConfig) | `node`, `iface` |
| NetworkService.cs | 2 SDN (RemoveSdnZone, RemoveSdnVnet) | `zone`, `vnet` |
| CloudInitService.cs | 3 (GetCloudInitConfig, SetCloudInitConfig, RegenerateCloudInitImage) | `node` |
| TaskService.cs | 2 (GetTask, GetTaskLog) | `node` |
| TemplateService.cs | 1 (CreateTemplate) | `node` |
Properly escaped services (no issues): VmService, ContainerService, SnapshotService, FirewallService, PoolService, UserService, StorageService.
### Other Code Quality Findings
- No TODO/FIXME/HACK comments
- No commented-out code
- No unused using directives detected
- HttpClient lifecycle is well-managed (1:1 ownership, proper disposal)
- `PveCmdletBase.WaitForStatusTransition` power-state polling loop is distinct from task polling (D001 compliant)
---
## Phase 4 — Testing Coverage Analysis
### Test Summary
| Category | Files | Tests |
|---|---|---|
| xUnit (C# unit) | 25 | 374 |
| Pester (PS unit) | 48 | 1,268 |
| Pester (integration) | 1 | 107 |
| **Total** | **74** | **1,749** |
### Coverage Highlights
- **100% cmdlet unit test coverage** — all 169 cmdlets have Pester parameter/metadata tests
- **xUnit service tests**: UserService (78), StorageService (40), FirewallModel (24), BackupService (23), SdnModel (21)
- **32 JSON fixture files** from real PVE 8/9 API responses for model deserialization tests
- **Integration tests**: 107 tests across connection, nodes, VMs, containers, snapshots, storage, users, firewall, SDN, backup, templates, cloud-init, guest agent
### Coverage Gaps (F046)
| Gap | Severity | Detail |
|---|---|---|
| No VmService xUnit tests | Medium | VM ops (clone, migrate, import) have complex logic untested at service layer |
| No ContainerService xUnit tests | Medium | Similar complexity to VmService |
| No NetworkService xUnit tests | Low | Thin CRUD wrappers |
| No FirewallService xUnit tests | Low | Thin CRUD wrappers |
| Pool/Cluster cmdlets lack integration tests | Low | Simple read-only operations |
---
## Phase 4b — CI Integration Test Results
### Last CI Run
| Field | Value |
|---|---|
| Run ID | 23461565300 |
| Conclusion | **SUCCESS** |
| Date | 2026-03-23T21:42:02Z |
| SHA | `1faaba0` |
| Branch | main |
All 7 jobs passed: container-image, build, provision, test(8), test(9), cleanup, cleanup-images.
### CI Findings
| Finding ID | Test / Cmdlet | Status | Notes |
|---|---|---|---|
| F080 | Restart-PveContainer | Resolved (scan-6) | ConfirmImpact.High fix applied |
| F081 | Copy-PveContainer | Resolved (scan-6) | Cascade from F080 |
| F082 | PVE 8 Docker image | **Resolved (this scan)** | Container-image job now succeeds |
---
## Phase 5 — Security Review
| Finding ID | Area | File(s) | Severity | Status | Description |
|---|---|---|---|---|---|
| F050 | URL encoding | 5 service files | Medium | **Regressed** | 20 instances of unescaped path params (see Phase 3) |
| F031 | Secrets | terraform.tfvars, CI | Low | Open | Lab IPs in terraform.tfvars (gitignored); CI uses ${{ secrets.* }} properly |
| — | Credential handling | All password cmdlets | Pass | — | SecureString with Marshal try/finally throughout |
| — | TLS/HTTPS | PveHttpClient.cs | Pass | — | HTTPS-only, SkipCertificateCheck opt-in with warning |
| — | CSRF | PveHttpClient.cs | Pass | — | CSRFPreventionToken on all mutating requests |
| — | Bare catches | All .cs files | Pass | — | No D004 violations |
| — | Boundary generation | PveHttpClient.cs | Pass | — | Uses RandomNumberGenerator (CSPRNG) |
| — | Dependencies | Both .csproj | Pass | — | Newtonsoft.Json 13.0.3, SharpCompress 0.47.3 (current) |
---
## Phase 6 — PSGallery Publication Readiness
| Finding ID | Check | Pass/Fail | Notes |
|---|---|---|---|
| — | ModuleVersion | Pass | 0.1.0, dynamically set from git tag |
| — | GUID | Pass | Present and unique |
| — | Author/CompanyName | Pass | goodolclint / Worklab |
| — | Description | Pass | Descriptive, mentions PVE 8.x/9.x |
| — | PowerShellVersion | Pass | 5.1 minimum |
| — | CompatiblePSEditions | Pass | Desktop + Core |
| — | CmdletsToExport | Pass | Explicit list of 169 cmdlets |
| — | Tags | Pass | 8 tags for discoverability |
| — | LicenseUri/ProjectUri | Pass | GitHub links |
| F021 | IconUri | **Fail** | Not set — PSGallery shows placeholder |
| — | ReleaseNotes | Pass | Present in PSData |
| — | Framework targets | Pass | netstandard2.0 for both publishable projects |
| — | Publish workflow | Pass | Tag-triggered, 3-job pipeline with PS 5.1 smoke test |
| — | CHANGELOG | Pass | Keep a Changelog format |
---
## Phase 7 — Community & Repo Maintenance Standards
| Finding ID | Check | Pass/Fail | Notes |
|---|---|---|---|
| — | Bug report template | Pass | YAML form with version fields |
| — | Feature request template | Pass | YAML form with use case |
| — | Issue template config | Pass | Disables blank issues, links to Discussions |
| — | PR template | Pass | Checklist with build, tests, psd1, changelog |
| — | CONTRIBUTING.md | Pass | Complete dev setup and coding standards |
| — | CODE_OF_CONDUCT.md | Pass | Contributor Covenant 2.1 |
| — | SECURITY.md | Pass | Responsible disclosure instructions |
| — | DECISIONS.md | Pass | 12 decisions, linked from CLAUDE.md |
| — | CODEOWNERS | Pass | `* @goodolclint` |
| — | .editorconfig | Pass | Per-filetype overrides |
| — | .gitattributes | Pass | LF enforcement, binary handling |
| — | Commit conventions | Pass | Conventional Commits consistently used |
| — | Release process | Pass | Tag-based publish with GitHub Release |
| — | Dependabot | Pass | Automated dependency PRs |
---
## Phase 9 — Prioritized Recommendations
### 🔴 Critical
No critical findings.
### 🟠 High
No high findings.
### 🟡 Medium
| Finding ID | What | Where | Why | Fix |
|---|---|---|---|---|
| F050 | **Uri.EscapeDataString missing (REGRESSED)** | NodeService, NetworkService, CloudInitService, TaskService, TemplateService | D003 violation — defense-in-depth URL encoding missing on 20 path parameter interpolations | Add `Uri.EscapeDataString()` to all `node`, `iface`, `zone`, `vnet` params in URL paths |
| F046 | Integration test coverage gaps | tests/ | VmService, ContainerService lack xUnit service-layer tests; Pools/Cluster lack integration tests | Add xUnit tests for VmService and ContainerService |
| F059 | VM/CT-level firewall 0% coverage | — | Firewall endpoints exist for VMs and containers but no cmdlets implemented | Add VM/CT-scoped firewall cmdlets |
| F060 | Cluster config 0% coverage | — | Cluster options, join/create, notifications, metrics uncovered | Add cluster config cmdlets |
| F061 | PVE 9.0 endpoints 0% coverage | — | 42 new endpoints (HA rules, bulk actions, SDN fabrics) not implemented | Prioritize HA rules and bulk actions |
### 🟢 Low
| Finding ID | What | Where | Why | Fix |
|---|---|---|---|---|
| F021 | No IconUri in manifest | PSProxmoxVE.psd1 | PSGallery listing looks less professional | Add 128x128 PNG icon and set IconUri |
| F031 | Hardcoded test IPs | terraform.tfvars, CI | Lab IPs committed (gitignored locally) | Verify tfvars not tracked in git |
| F053 | HA subsystem 0% coverage | — | No HA cmdlets | Implement when HA rules (PVE 9.0) are prioritized |
| F054 | Ceph subsystem 0% coverage | — | Large specialized surface | Low priority unless Ceph users identified |
| F067 | Disk management 0% coverage | — | Node disk ops (LVM, ZFS, init) | Useful for provisioning workflows |
| F068 | Notifications 0% coverage | — | 25+ endpoints (PVE 8.1+) | Medium-value for operational users |
| F069 | ACME/Certificates 0% coverage | — | Certificate management | Low priority |