Files
PSProxmoxVE/docs/review/REVIEW_REPORT.md
T
Clint Branham f311c738b4 docs: review scan-8 findings update
Scan-8 full re-scan of PSProxmoxVE module (2026-03-24).

New findings:
- F084 [medium/security]: PveSession exposes auth secrets in pipeline output

Regressed:
- F039 [medium]: Bare catch blocks in VmService.PingGuestAgent and
  ImportPveOvaCmdlet (D004 violation)

Resolved:
- F059: VM/CT-level firewall was already supported via Level parameter

Findings DB: F001-F084, 10 open + 1 regressed + 72 resolved + 1 wont_fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:15:46 -05:00

20 KiB
Raw Blame History

PSProxmoxVE Review Report — Scan 8

Scan date:           2026-03-24
Prior report date:   2026-03-23
PVE API spec date:   2026-03-21T15:04:50.641Z
PVE API spec SHA256: 4af79be30166209a4714b771f65e1e9540c5b738f414ff30c98454402e29d030
PVE version hint:    (not set)
Total API endpoints: 646
Findings DB:         docs/review/findings.json (F001F084)
Open findings:       10 (before scan) → 11 (after scan, including 1 regressed)
New this scan:       1  Resolved this scan: 1 (F059)  Regressed: 1 (F039)
Last CI run:         integration-tests.yml | failure (provision infra) | 2026-03-24 | run 23511293737
                     (newer run in_progress — prior successful run: 23511223201)

Executive Summary

  • Delta: 1 resolved (F059) | 1 new (F084) | 1 regressed (F039) | 11 open (10 open + 1 regressed)
  • API drift: 24 breaking parameter changes in PVE 9.0 (mostly cluster config link[n] type changes) | 42 new PVE 9.0 endpoints unimplemented
  • CI: Last completed integration run: FAILED (provision infrastructure — cloud image download race condition, already resolved in subsequent run) | 0 test failures
  • Module has 169 cmdlets covering ~155 of 646 API endpoints (24% coverage)
  • All prior code quality findings (F032F044, F049F051, F058, F062F063) remain resolved — no regressions detected
  • All DECISIONS.md patterns verified: no bare catches, no System.Text.Json, no plain string passwords, no inline task polling, URL encoding applied, all cmdlets sealed with OutputType
  • PSGallery publication readiness is high — manifest complete, publish workflow in place, PS 5.1 smoke test passes
  • 12 open findings: 1 regression (F039 bare catch blocks in VmService + ImportPveOvaCmdlet), 1 new security finding (F084 PveSession exposes auth secrets), API coverage gaps, and manifest cosmetic issue (F021 IconUri)

Phase 1 — Repository Inventory & Structure

Project Layout

Item Present Path Notes
Solution file Yes PSProxmoxVE.sln 2 src projects + 1 test project
Module project Yes src/PSProxmoxVE/ netstandard2.0, 169 cmdlets
Core project Yes src/PSProxmoxVE.Core/ netstandard2.0, services + models
xUnit tests Yes tests/PSProxmoxVE.Core.Tests/ net10.0 + net48
Pester tests Yes tests/PSProxmoxVE.Tests/ 30+ test files
Integration tests Yes tests/PSProxmoxVE.Tests/Integration/ PVE 8 + PVE 9
Module manifest Yes src/PSProxmoxVE/PSProxmoxVE.psd1 v0.1.0-preview
MAML help Yes src/PSProxmoxVE/PSProxmoxVE.dll-Help.xml Generated
Format file Yes src/PSProxmoxVE/PSProxmoxVE.format.ps1xml Custom formatting
Cmdlet docs Yes docs/cmdlets/ 169 markdown files
README.md Yes README.md Comprehensive, badges
CHANGELOG.md Yes CHANGELOG.md Keep-a-Changelog format
CONTRIBUTING.md Yes CONTRIBUTING.md Development setup + guidelines
LICENSE Yes LICENSE MIT
CODE_OF_CONDUCT.md Yes CODE_OF_CONDUCT.md Contributor Covenant
SECURITY.md Yes SECURITY.md Security policy
DECISIONS.md Yes DECISIONS.md 12 architectural decisions
CODEOWNERS Yes CODEOWNERS @goodolclint
.editorconfig Yes .editorconfig Consistent style
.gitignore Yes .gitignore Comprehensive
.gitattributes Yes .gitattributes Present
Issue templates Yes .github/ISSUE_TEMPLATE/ Bug report + feature request + config.yml
PR template Yes .github/pull_request_template.md Present
Dependabot Yes .github/dependabot.yml NuGet + GitHub Actions weekly

CI/CD Workflows

Workflow File Trigger Status
Build build.yml push/PR to main matrix: windows+ubuntu, net48+net10.0
Unit Tests unit-tests.yml push/PR to main Pester on PS 5.1, 7.5 (win/ubuntu/macos)
Integration Tests integration-tests.yml push to main Self-hosted, PVE 8+9, concurrency group
Publish publish.yml tag push (v*) PSGallery + GitHub Release

Missing Items

Finding ID Item Notes
F021 IconUri in manifest PSData Cosmetic — recommended for PSGallery listing

Phase 2 — PVE API Coverage Audit

Coverage by Functional Area

Area Total Endpoints Covered % New in 9.0 (unimpl) Notes
access 15 7 47% 1 API tokens, permissions, password
access_domains 6 4 67% 0 CRUD complete
access_groups 5 4 80% 0 CRUD complete
acl 2 0 0% 0
acme 15 0 0% 0 F069
apt 8 0 0% 0 Node package management
backup 6 6 100% 0 Full coverage
ceph 40 0 0% 0 F054
certificates 8 0 0% 0 F069
cluster 77 ~1 1% 6 Only Get-PveClusterResource
cluster_config 10 0 0% 0 F060
containers 62 ~25 40% 1 Good lifecycle coverage
disks 18 0 0% 0 F067
firewall 40 ~20 50% 0 All levels (cluster/node/VM/CT) via Level param; F059 resolved
ha 21 0 0% 5 F053
metrics 7 0 0% 0
networking 7 5 71% 0 Good coverage
nodes 75 ~8 11% 11 Basic ops covered
other 1 0 0% 0
pools 7 5 71% 0 CRUD + update
replication 5 0 0% 0
roles 5 4 80% 0 CRUD complete
sdn 60 ~19 32% 16 Zones/vnets/subnets/IPAM/DNS/ctrl
services 7 0 0% 0 Node service management
storage 19 ~11 58% 1 Good coverage
storage_config 5 0 0% 0 Separate from storage CRUD
tasks 5 4 80% 0 Good coverage
users 12 4 33% 0 CRUD only
version 1 1 100% 0 Via Connect-PveServer
vms 97 ~30 31% 1 Good lifecycle + guest agent
TOTAL 646 ~155 ~24% 42

API Drift — PVE 9.0 Breaking Changes

24 endpoints have breaking parameter changes in PVE 9.0. Most are link[n] type changes on cluster config endpoints (not covered by this module). Relevant changes affecting implemented endpoints:

Endpoint Change Module Cmdlet Risk
POST /nodes/{node}/qemu machine param type changed New-PveVm Low
POST /nodes/{node}/qemu/{vmid}/config param changes Set-PveVmConfig Low
PUT /nodes/{node}/qemu/{vmid}/config param changes Set-PveVmConfig Low
POST /nodes/{node}/qemu/{vmid}/status/start param changes Start-PveVm Low
POST /nodes/{node}/lxc param changes New-PveContainer Low
PUT /nodes/{node}/lxc/{vmid}/config param changes Set-PveContainerConfig Low
POST /cluster/backup param changes New-PveBackupJob Low
PUT /cluster/backup/{id} param changes Set-PveBackupJob Low
PUT /cluster/firewall/options log_ratelimit changed Set-PveFirewallOptions Low
POST /nodes/{node}/vzdump param changes New-PveBackup Low
PUT /nodes/{node}/network param changes Set-PveNetwork Low
PUT /nodes/{node}/config param changes Set-PveNodeConfig Low
POST /storage param changes New-PveStorage Medium
PUT /storage/{storage} param changes Set-PveStorage Medium

Assessment: No high-risk breaking changes for existing cmdlets. The link[n] parameter type changes affect cluster config endpoints not yet implemented. Storage param changes add new storage backend options (additive in practice).

PVE 9.0 New Endpoints (42 total, 0 implemented)

Area Count Notable Endpoints
SDN 16 Fabric management (new subsystem in 9.0)
Nodes 11 Service state, vzdump defaults, disk management
Cluster 6 SDN apply, HA resources/rules, metrics export
HA 5 Rules CRUD + resource migrate/relocate
VMs 1 Migration endpoint enhancement
Containers 1 Migration endpoint enhancement
Storage 1 Storage endpoint enhancement
Access 1 Access endpoint enhancement

High-Value Gaps

  1. Ceph management (40 endpoints) — Critical for hyperconverged deployments
  2. HA management (21 endpoints, 5 new in 9.0) — Critical for production clusters
  3. VM/CT-level firewall (~23 endpoints) — Cluster-level done, per-VM/CT missing
  4. Disk management (18 endpoints) — ZFS, LVM, directory operations
  5. Cluster configuration (10 endpoints) — Join/create/manage cluster

Phase 3 — Code Quality & Best Practices

3a. PowerShell Module Design

All 169 cmdlets verified:

  • All cmdlet classes are sealed
  • All cmdlets have [OutputType] attribute
  • All destructive cmdlets have ConfirmImpact = ConfirmImpact.High (including Restart/Suspend-PveContainer, F062/F063 resolved)
  • VmId parameters use [ValidateRange(100, 999999999)]
  • Verb class constants used (not string literals)
  • ShouldProcess on destructive cmdlets
  • HelpMessage on parameters
  • Pipeline support via ValueFromPipelineByPropertyName

3b. C# Code Quality

  • No inline task-polling loops — all cmdlets use TaskService.WaitForTask (F032/F033/F036/F058 resolved)
  • REGRESSION F039: Two bare catch {} blocks found (see below)
  • Password parameters use SecureString (F051 resolved)
  • URL paths use Uri.EscapeDataString() — 164 occurrences across 36 files (F050/F071 resolved)
  • No System.Text.Json attributes (F044 resolved)
  • Magic strings extracted to constants (F049 resolved)
  • GetAwaiter().GetResult() sync-over-async pattern — accepted per F048/wont_fix
  • Cryptographic RNG for boundary generation (F026 resolved)

InvokePveVmGuestExecCmdlet has an inline do/while loop for guest exec status polling. This is polling the QEMU guest agent exec status endpoint (not a PVE UPID task), with a proper timeout via Stopwatch + TimeSpan.FromSeconds(Timeout). Correctly does not use TaskService.WaitForTask.

3c. General Hygiene

  • No TODO/FIXME/HACK markers in source
  • No dead code or commented-out blocks found
  • Framework targeting correct: publishable projects use netstandard2.0, test project uses net10.0;net48
  • .editorconfig present with consistent style rules
  • 169 markdown cmdlet docs matching cmdlet count

3d. HttpClient Lifecycle

PveHttpClient creates a new HttpClient per instance. Services use _injectedClient ?? new PveHttpClient(session) — per-call when no injected client. F045 resolved by adding DI support (IPveHttpClient interface). The per-call pattern in production is acceptable for a PowerShell module where sessions are short-lived.

F039 — Bare Catch Blocks (REGRESSED)

Two bare catch blocks violating D004 were found:

  1. VmService.PingGuestAgent (VmService.cs:553): catch { return false; } — catches all exceptions including OutOfMemoryException. Should use catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException).

  2. ImportPveOvaCmdlet (ImportPveOvaCmdlet.cs:277): catch { /* fallback */ } — bare catch in VM retrieval fallback after OVA import. Same fix needed.

Code Quality Findings

Finding ID Severity Status Description
F039 Medium Regressed 2 bare catch blocks in VmService + ImportPveOvaCmdlet
F032 Critical Resolved Inline task polling → WaitForTask
F033 Critical Resolved Guest exec polling → timeout added
F058 Critical Resolved Container/storage polling → WaitForTask
F041 Medium Resolved Unsealed cmdlets → all sealed
F037 Medium Resolved Missing OutputType → all added
F044 Medium Resolved Dual JSON attributes → Newtonsoft only
F045 Medium Resolved HttpClient per-call → DI support added
F062 Medium Resolved Restart-PveContainer ConfirmImpact → High
F063 Medium Resolved Suspend-PveContainer ConfirmImpact → High

Phase 4 — Testing Coverage Analysis

Test Infrastructure

Component Framework Location Count
xUnit unit tests xunit 2.9.3, Moq 4.20.72 tests/PSProxmoxVE.Core.Tests/ ~12 test files
Pester unit tests Pester 5.x tests/PSProxmoxVE.Tests/ ~30 test files
Integration tests Pester 5.x tests/PSProxmoxVE.Tests/Integration/ 2 files

xUnit Coverage

Tests cover:

  • Authentication: PveAuthenticator, PveSession, PveVersion
  • Model deserialization: All model areas (Backup, Cluster, Container, Firewall, Network, Node, SDN, Snapshot, Storage, Task, User, VM)
  • Services: Backup, CloudInit, Cluster, Node, Pool, Snapshot, Storage, Task, Template, User

Gap (F046): 5 services lack xUnit tests: VmService, ContainerService, FirewallService, NetworkService, SdnService. These are the most complex service classes and are only covered at the Pester cmdlet-existence level and integration tests.

Additional test quality observations:

  • No error-response handling tests (HTTP 400/401/403/500 responses not exercised)
  • PoolServiceTests lacks null-guard tests (unlike all other service tests)
  • No negative integration tests (duplicate resource creation, nonexistent resource removal)
  • Integration gaps: Pool CRUD, Group/Domain CRUD, Cluster resources, most SDN Set cmdlets, guest agent extensions, Node config/DNS

Pester Test Coverage

Area Unit Tests Integration Tests
Connection Yes Yes
VMs Yes Yes
Containers Yes Yes
Storage Yes Yes
Network/SDN Yes Yes
Firewall Yes Yes
Backup Yes Yes
Tasks Yes Yes
Users/Roles Yes Yes
Pools Yes Yes
Templates Yes Yes
Nodes Yes Yes
Snapshots Yes Yes
CloudInit Yes Yes
OVA Import Yes Yes

Test Quality

  • Pester tests use Describe/Context/It structure with descriptive names
  • Integration tests tagged with @("Integration") for filtering
  • Connection details injected via environment variables
  • Integration tests cover both PVE 8 and PVE 9
  • xUnit tests use Moq for HTTP client mocking with realistic fixtures
  • Test cleanup via AfterAll blocks

Phase 4b — CI Integration Test Results

Most Recent Completed Run

Field Value
Run ID 23511293737
Conclusion failure
Created 2026-03-24T20:42:58Z
Head SHA 0f3e2c1
Branch main

Root cause: Cloud image download race condition — mv: cannot stat '...noble-server-cloudimg-amd64.qcow2.downloading'. Transient infrastructure issue. Prior run (23511223201) succeeded. Newer run (23511879409) in progress.

No test failures — tests were skipped due to provision failure.


Phase 5 — Security Review

Finding ID Area Status Notes
Credential handling Pass SecureString for all passwords, PSCredential for Connect
F084 Session object exposure New PveSession exposes Ticket/ApiToken/CsrfToken in default pipeline output
TLS/HTTPS Pass Enforced by default, SkipCertificateCheck opt-in with warning
Input validation Pass Uri.EscapeDataString on all path params, ValidateRange on IDs
Secret scanning Pass No secrets in committed files, .gitignore covers sensitive files
Dependency security Pass All packages current, Dependabot configured

F084 — PveSession Exposes Auth Secrets in Pipeline Output (NEW)

PveSession.Ticket, ApiToken, and CsrfToken are public string properties with no format-file hiding. When Connect-PveServer -PassThru or Test-PveConnection -Detailed writes the session to the pipeline, all secret fields are visible in the default table/list view. A user piping $session | Format-List * or logging verbose output could inadvertently expose auth tokens.

Fix: Add a PSProxmoxVE.format.ps1xml entry for PveSession that shows only Hostname, Port, AuthMode, IsExpired, and ServerVersion by default.


Phase 6 — PSGallery Publication Readiness

Finding ID Check Pass/Fail Notes
RootModule Pass PSProxmoxVE.dll
ModuleVersion Pass 0.1.0 (updated from tag by publish workflow)
GUID Pass a3f7c2d1-84e5-4b9f-a061-3e2d8c5f1a7b
Author Pass goodolclint
Description Pass Comprehensive description
PowerShellVersion Pass 5.1
CompatiblePSEditions Pass Desktop, Core
CmdletsToExport Pass 169 cmdlets explicitly listed
Tags Pass 8 relevant tags
LicenseUri Pass GitHub MIT license link
ProjectUri Pass GitHub repo link
ReleaseNotes Pass Present in PSData
F021 IconUri Fail Not set — recommended for PSGallery listing
Prerelease Pass 'preview' — appropriate for current state
Framework target Pass netstandard2.0 for publishable projects
PS 5.1 smoke test Pass Verifies >= 150 commands load
MAML help Pass PSProxmoxVE.dll-Help.xml present

Phase 7 — Community & Repo Maintenance Standards

Check Pass/Fail Notes
CONTRIBUTING.md Pass Development setup, PR process
CODE_OF_CONDUCT.md Pass Contributor Covenant
SECURITY.md Pass Security policy
DECISIONS.md Pass 12 active decisions
CODEOWNERS Pass @goodolclint
Issue templates Pass Bug report + feature request + config.yml
PR template Pass Present
Dependabot Pass NuGet + GitHub Actions weekly
Branch protection Pass Required checks + review
Commit conventions Pass Conventional commits
Release process Pass Tag-triggered publish
CHANGELOG Pass Keep-a-Changelog

Phase 8 — Findings Database Update

Verification of DECISIONS.md Patterns

Decision Pattern Verified Method
D001 TaskService.WaitForTask Yes grep while(true) — only in TaskService + WaitPveTask cmdlet
D002 SecureString passwords Yes grep public string Password — none found
D003 Uri.EscapeDataString Yes 164 occurrences across 36 files
D004 No bare catches FAIL 2 bare catches found: VmService.cs:553, ImportPveOvaCmdlet.cs:277 → F039 regressed
D005 OutputType on all cmdlets Yes 169 cmdlets, 169 OutputType attributes
D006 ConfirmImpact.High Yes All destructive cmdlets including Restart/Suspend-Container
D007 Sealed cmdlet classes Yes All 169 classes sealed
D008 Newtonsoft.Json only Yes No System.Text.Json references in src/
D009 netstandard2.0 targets Yes Both publishable projects target netstandard2.0
D010 ValidateRange on VmId Yes All VmId parameters have ValidateRange
D011 Verb class constants Yes No string literal verbs found
D012 Magic string constants Yes ApiTokenPrefix, CsrfHeaderName constants in PveHttpClient

1 regression detected: F039 (bare catch blocks) — D004 violated in 2 locations. No new decisions needed.


Phase 9 — Prioritized Recommendations

Medium (5 findings)

Finding ID What Where Why Fix
F039 REGRESSED Bare catch blocks VmService.cs:553, ImportPveOvaCmdlet.cs:277 Catches all exceptions including OOM/SOE, violates D004 Replace with catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException)
F084 PveSession exposes auth secrets PveSession.cs, format.ps1xml Ticket/ApiToken/CsrfToken visible in pipeline output Add format.ps1xml entry hiding secret properties
F046 Integration test coverage gaps tests/ VmService, ContainerService lack xUnit tests Add xUnit tests with Moq for complex services
F060 Cluster config 0% src/ Cannot manage clusters Add cluster config cmdlets
F061 PVE 9.0 endpoints 0% src/ 42 new endpoints, none implemented Prioritize SDN fabrics + HA rules

Low (6 findings)

Finding ID What Where Why Fix
F021 No IconUri in manifest PSProxmoxVE.psd1 PSGallery listing lacks icon Add IconUri pointing to project logo
F053 HA subsystem 0% src/ 21 endpoints for HA management Implement HA group/resource/rule cmdlets
F054 Ceph subsystem 0% src/ 40 endpoints for hyperconverged Implement Ceph pool/OSD/monitor cmdlets
F067 Disk management 0% src/ ZFS/LVM/directory ops missing Implement disk management cmdlets
F068 Notifications 0% src/ Notification targets not managed Implement notification cmdlets
F069 ACME/Certificates 0% src/ Let's Encrypt + cert mgmt missing Implement ACME/cert cmdlets