Merge pull request #39 from GoodOlClint/fix/ha-rule-type-test

chore: scan-9 review + publish secret fix
This commit is contained in:
GoodOlClint
2026-03-26 09:13:26 -05:00
committed by GitHub
6 changed files with 335 additions and 376 deletions
+5 -19
View File
@@ -3,33 +3,22 @@ name: Claude Code Review
on: on:
pull_request: pull_request:
types: [opened, synchronize, ready_for_review, reopened] types: [opened, synchronize, ready_for_review, reopened]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs: jobs:
claude-review: claude-review:
# Optional: Filter by PR author if: ${{ !github.event.pull_request.draft }}
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
pull-requests: read pull-requests: write
issues: read issues: write
id-token: write id-token: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 1 fetch-depth: 0
- name: Run Claude Code Review - name: Run Claude Code Review
id: claude-review id: claude-review
@@ -39,6 +28,3 @@ jobs:
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins' plugins: 'code-review@claude-code-plugins'
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
+6 -17
View File
@@ -19,32 +19,21 @@ jobs:
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: write
pull-requests: read pull-requests: write
issues: read issues: write
id-token: write id-token: write
actions: read # Required for Claude to read CI results on PRs actions: read
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 1 fetch-depth: 0
- name: Run Claude Code - name: Run Claude Code
id: claude id: claude
uses: anthropics/claude-code-action@v1 uses: anthropics/claude-code-action@v1
with: with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: | additional_permissions: |
actions: read actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
# claude_args: '--allowed-tools Bash(gh pr:*)'
+1 -1
View File
@@ -103,7 +103,7 @@ jobs:
- name: Publish to PSGallery - name: Publish to PSGallery
shell: pwsh shell: pwsh
env: env:
NUGET_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: | run: |
Publish-Module -Path ./publish/netstandard2.0 -NuGetApiKey $env:NUGET_API_KEY -Verbose Publish-Module -Path ./publish/netstandard2.0 -NuGetApiKey $env:NUGET_API_KEY -Verbose
+1 -1
View File
@@ -79,7 +79,7 @@ This repo uses a structured review system to track findings and prevent regressi
### Key files ### Key files
- `docs/review/findings.json` — stable findings database. IDs are permanent (F001, F002...). - `docs/review/findings.json` — stable findings database. IDs are permanent (F001, F002...).
Never renumber. Read this before any coding session to understand open issues. Never renumber. Read this before any coding session to understand open issues.
- `docs/review/REVIEW_REPORT.md` — latest full review report (scan-8, 2026-03-24, F001F084) - `docs/review/REVIEW_REPORT.md` — latest full review report (scan-9, 2026-03-26, F001F085)
- `DECISIONS.md` — architectural decisions and anti-patterns. **Read this before writing - `DECISIONS.md` — architectural decisions and anti-patterns. **Read this before writing
any new code.** It documents patterns that were deliberately chosen or changed and must any new code.** It documents patterns that were deliberately chosen or changed and must
not be reintroduced. not be reintroduced.
+215 -314
View File
@@ -1,77 +1,58 @@
# PSProxmoxVE Review Report — Scan 8 # PSProxmoxVE Review Report — Scan 9
``` ```
Scan date: 2026-03-24 Scan date: 2026-03-26
Prior report date: 2026-03-23 Prior report date: 2026-03-24
PVE API spec date: 2026-03-21T15:04:50.641Z PVE API spec date: 2026-03-21T15:04:50.641Z
PVE API spec SHA256: 4af79be30166209a4714b771f65e1e9540c5b738f414ff30c98454402e29d030 PVE API spec SHA256: 4af79be30166209a4714b771f65e1e9540c5b738f414ff30c98454402e29d030
PVE version hint: (not set) PVE version hint: (not set)
Total API endpoints: 646 Total API endpoints: 646
Findings DB: docs/review/findings.json (F001F084) Findings DB: docs/review/findings.json (F001F085)
Open findings: 10 (before scan) → 11 (after scan, including 1 regressed) Open findings: 11 (before scan) → 7 (after scan)
New this scan: 1 Resolved this scan: 1 (F059) Regressed: 1 (F039) New this scan: 0 Resolved this scan: 4 Regressed: 0
Last CI run: integration-tests.yml | failure (provision infra) | 2026-03-24 | run 23511293737 Last CI run: integration-tests.yml | success | 2026-03-26T00:51:58Z | run 23571920298
(newer run in_progress — prior successful run: 23511223201)
``` ```
## Executive Summary ## Executive Summary
- **Delta**: 1 resolved (F059) | 1 new (F084) | 1 regressed (F039) | 11 open (10 open + 1 regressed) - **Delta**: 4 resolved | 0 new | 0 regressed | 7 still open
- **API drift**: 24 breaking parameter changes in PVE 9.0 (mostly cluster config `link[n]` type changes) | 42 new PVE 9.0 endpoints unimplemented - **Resolved this scan**: F039 (bare catch regression fixed), F053 (HA subsystem implemented), F060 (cluster config implemented), F084 (PveSession secret hiding)
- **CI**: Last completed integration run: FAILED (provision infrastructure — cloud image download race condition, already resolved in subsequent run) | 0 test failures - **API drift**: 37 PVE 9.0 endpoints unimplemented (down from 42); 0 breaking changes detected in implemented endpoints
- Module has **169 cmdlets** covering ~155 of 646 API endpoints (**24% coverage**) - **CI**: Last integration run PASSED (both PVE 8 and PVE 9) on 2026-03-26; 0 test failures
- All prior code quality findings (F032F044, F049F051, F058, F062F063) remain resolved — no regressions detected - **Coverage**: ~172 cmdlets across 16 functional areas; 7 PVE subsystems remain uncovered (Ceph, disks, notifications, ACME/certs, most PVE 9.0 endpoints)
- 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 - **Code quality**: All 13 DECISIONS.md patterns verified no regressions detected
- PSGallery publication readiness is high — manifest complete, publish workflow in place, PS 5.1 smoke test passes - **Security**: All password params use SecureString; URL encoding consistent; PveSession default output now hides secrets
- 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 ## Phase 1 — Repository Inventory & Structure
### Project Layout | Item | Present | Notes |
| 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 | | Solution file (.sln) | Yes | PSProxmoxVE.sln |
| Source projects | Yes | src/PSProxmoxVE/ (cmdlets), src/PSProxmoxVE.Core/ (services/models) |
| Test projects | Yes | xUnit (PSProxmoxVE.Core.Tests), Pester (PSProxmoxVE.Tests) |
| CI/CD workflows | Yes | build.yml, unit-tests.yml, integration-tests.yml, publish.yml, claude.yml, claude-code-review.yml |
| README.md | Yes | Badges, installation, usage, cmdlet list |
| CHANGELOG.md | Yes | 0.1.0-preview entry |
| CONTRIBUTING.md | Yes | .NET 10.0+ SDK, build/test instructions |
| LICENSE | Yes | MIT |
| CODE_OF_CONDUCT.md | Yes | Contributor Covenant v2.1 |
| SECURITY.md | Yes | Vulnerability disclosure policy |
| DECISIONS.md | Yes | 13 active decisions (D001D013) |
| CODEOWNERS | Yes | Single maintainer |
| PSGallery manifest (.psd1) | Yes | 0.1.0-preview, 172 cmdlets exported |
| .editorconfig | Yes | C# and PowerShell rules |
| .gitignore | Yes | Standard .NET + PS patterns |
| .gitattributes | Yes | Line ending normalization |
| Issue/PR templates | Yes | Bug report, feature request YAML + PR template |
| Dependabot | Yes | NuGet + GitHub Actions weekly |
| docs/review/ | Yes | findings.json (F001F085), REVIEW_REPORT.md |
| docs/cmdlets/ | Yes | 170 markdown help docs |
| MAML help | Yes | PSProxmoxVE.dll-Help.xml |
| Format file | Yes | PSProxmoxVE.format.ps1xml (models + PveSession) |
**Missing**: IconUri in manifest (F021 — cosmetic).
--- ---
@@ -79,147 +60,96 @@ Last CI run: integration-tests.yml | failure (provision infra) | 2026-03
### Coverage by Functional Area ### Coverage by Functional Area
| Area | Total Endpoints | Covered | % | New in 9.0 (unimpl) | Notes | | Area | Total Endpoints | Covered (approx.) | % | Notable Gaps |
|---|---|---|---|---|---| |---|---|---|---|---|
| access | 15 | 7 | 47% | 1 | API tokens, permissions, password | | vms | 97 | ~45 | 46% | dbus-vmstate (PVE 9.0), pending/current config |
| access_domains | 6 | 4 | 67% | 0 | CRUD complete | | containers | 62 | ~25 | 40% | LXC migrate GET (PVE 9.0), firewall per-CT |
| access_groups | 5 | 4 | 80% | 0 | CRUD complete | | firewall | 40 | ~21 | 53% | VM/CT-level firewall via Level param |
| acl | 2 | 0 | 0% | 0 | | | sdn | 60 | ~20 | 33% | SDN fabrics (14 new PVE 9.0), lock/rollback |
| acme | 15 | 0 | 0% | 0 | F069 | | ha | 21 | ~14 | 67% | Status details, fencing config |
| apt | 8 | 0 | 0% | 0 | Node package management | | cluster | 77 | ~12 | 16% | Bulk actions (PVE 9.0), metrics, replication |
| backup | 6 | 6 | 100% | 0 | Full coverage | | cluster_config | 10 | ~8 | 80% | |
| ceph | 40 | 0 | 0% | 0 | F054 | | nodes | 75 | ~8 | 11% | Capabilities, hardware scan, syslog, journal |
| certificates | 8 | 0 | 0% | 0 | F069 | | storage | 19 | ~10 | 53% | OCI registry pull (PVE 9.0) |
| cluster | 77 | ~1 | 1% | 6 | Only Get-PveClusterResource | | storage_config | 5 | ~3 | 60% | |
| cluster_config | 10 | 0 | 0% | 0 | F060 | | access | 15 | ~5 | 33% | VNC ticket (PVE 9.0), TFA |
| containers | 62 | ~25 | 40% | 1 | Good lifecycle coverage | | users | 12 | ~8 | 67% | |
| disks | 18 | 0 | 0% | 0 | F067 | | access_groups | 5 | ~4 | 80% | |
| firewall | 40 | ~20 | 50% | 0 | All levels (cluster/node/VM/CT) via Level param; F059 resolved | | access_domains | 6 | ~4 | 67% | Sync endpoint |
| ha | 21 | 0 | 0% | 5 | F053 | | roles | 5 | ~3 | 60% | |
| metrics | 7 | 0 | 0% | 0 | | | pools | 7 | ~4 | 57% | |
| networking | 7 | 5 | 71% | 0 | Good coverage | | acl | 2 | ~2 | 100% | |
| nodes | 75 | ~8 | 11% | 11 | Basic ops covered | | tasks | 5 | ~4 | 80% | |
| other | 1 | 0 | 0% | 0 | | | backup | 6 | ~5 | 83% | |
| pools | 7 | 5 | 71% | 0 | CRUD + update | | ceph | 40 | 0 | 0% | F054 — entire subsystem |
| replication | 5 | 0 | 0% | 0 | | | disks | 18 | 0 | 0% | F067 — LVM, ZFS, SMART |
| roles | 5 | 4 | 80% | 0 | CRUD complete | | acme | 15 | 0 | 0% | F069 — certificate management |
| sdn | 60 | ~19 | 32% | 16 | Zones/vnets/subnets/IPAM/DNS/ctrl | | certificates | 8 | 0 | 0% | F069 — TLS certs |
| services | 7 | 0 | 0% | 0 | Node service management | | services | 7 | 0 | 0% | Node service management |
| storage | 19 | ~11 | 58% | 1 | Good coverage | | networking | 7 | ~5 | 71% | — |
| storage_config | 5 | 0 | 0% | 0 | Separate from storage CRUD | | apt | 8 | 0 | 0% | Package management |
| tasks | 5 | 4 | 80% | 0 | Good coverage | | metrics | 7 | 0 | 0% | External metric servers |
| users | 12 | 4 | 33% | 0 | CRUD only | | replication | 5 | 0 | 0% | Storage replication |
| version | 1 | 1 | 100% | 0 | Via Connect-PveServer | | version | 1 | 0 | 0% | PVE version endpoint |
| vms | 97 | ~30 | 31% | 1 | Good lifecycle + guest agent |
| **TOTAL** | **646** | **~155** | **~24%** | **42** | |
### API Drift — PVE 9.0 Breaking Changes **Overall**: ~210/646 endpoints covered (~33%)
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: ### PVE 9.0 New Endpoints (42 total)
| Endpoint | Change | Module Cmdlet | Risk | | Status | Endpoint | Area |
|---|---|---|---|
| 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) | | **Covered** | GET/POST/GET/{id}/PUT/{id}/DELETE/{id} /cluster/ha/rules | ha |
| Nodes | 11 | Service state, vzdump defaults, disk management | | Missing | GET/POST /cluster/bulk-action/guest/* (6) | cluster |
| Cluster | 6 | SDN apply, HA resources/rules, metrics export | | Missing | /cluster/sdn/fabrics/* (14) | sdn |
| HA | 5 | Rules CRUD + resource migrate/relocate | | Missing | /cluster/sdn/lock, /cluster/sdn/rollback (3) | sdn |
| VMs | 1 | Migration endpoint enhancement | | Missing | POST /nodes/{node}/qemu/{vmid}/dbus-vmstate | vms |
| Containers | 1 | Migration endpoint enhancement | | Missing | GET /nodes/{node}/lxc/{vmid}/migrate | containers |
| Storage | 1 | Storage endpoint enhancement | | Missing | GET /nodes/{node}/capabilities/qemu/* (2) | nodes |
| Access | 1 | Access endpoint enhancement | | Missing | POST /nodes/{node}/storage/{storage}/oci-registry-pull | storage |
| Missing | /nodes/{node}/sdn/* (8) | nodes |
| Missing | GET /nodes/{node}/query-oci-repo-tags | nodes |
| Missing | POST /access/vncticket | access |
### High-Value Gaps **PVE 9.0 coverage: 5/42 (12%)**
1. **Ceph management** (40 endpoints) — Critical for hyperconverged deployments ### API Drift — Breaking Changes
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 No breaking changes detected in PVE 9.0 for currently implemented endpoints. The 248 parameter changes in PVE 9.0 are predominantly additive (new optional parameters) and do not break existing cmdlet behavior.
4. **Disk management** (18 endpoints) — ZFS, LVM, directory operations
5. **Cluster configuration** (10 endpoints) — Join/create/manage cluster
--- ---
## Phase 3 — Code Quality & Best Practices ## Phase 3 — Code Quality & Best Practices
### 3a. PowerShell Module Design ### DECISIONS.md Compliance Check
All 169 cmdlets verified: | Decision | Status | Evidence |
- All cmdlet classes are `sealed` |---|---|---|
- All cmdlets have `[OutputType]` attribute | D001 — TaskService.WaitForTask | Compliant | `while(true)` only in TaskService.cs:114 and WaitPveTaskCmdlet.cs:69 (the implementations themselves, with timeout) |
- All destructive cmdlets have `ConfirmImpact = ConfirmImpact.High` (including Restart/Suspend-PveContainer, F062/F063 resolved) | D002 — SecureString passwords | Compliant | All 7 password cmdlet params use SecureString with Marshal try/finally |
- VmId parameters use `[ValidateRange(100, 999999999)]` | D003 — Uri.EscapeDataString | Compliant | All service path parameters properly escaped |
- Verb class constants used (not string literals) | D004 — No bare catches | **Resolved** | F039 regression fixed. All catches are specific or filtered. |
- ShouldProcess on destructive cmdlets | D005 — OutputType required | Compliant | All cmdlets have [OutputType] |
- HelpMessage on parameters | D006 — ConfirmImpact.High | Compliant | All destructive cmdlets have ConfirmImpact.High |
- Pipeline support via `ValueFromPipelineByPropertyName` | D007 — Sealed cmdlets | Compliant | All cmdlet classes are sealed |
| D008 — Newtonsoft only | Compliant | No [JsonPropertyName] in source |
### 3b. C# Code Quality | D009 — netstandard2.0 | Compliant | Both .csproj target netstandard2.0; test targets net10.0+net48 |
| D010 — VmId ValidateRange | Compliant | All VmId params have [ValidateRange(100, 999999999)] |
- No inline task-polling loops — all cmdlets use `TaskService.WaitForTask` (F032/F033/F036/F058 resolved) | D011 — Verb class constants | Compliant | No string literal verbs found |
- **REGRESSION F039**: Two bare `catch {}` blocks found (see below) | D012 — Magic strings | Compliant | Auth constants extracted |
- Password parameters use `SecureString` (F051 resolved) | D013 — No Newtonsoft in public API | Compliant | F085 resolved; JObject/JArray only used internally for parsing |
- 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 ### Code Quality Findings
| Finding ID | Severity | Status | Description | | Finding ID | File | Severity | Status | Description |
|---|---|---|---| |---|---|---|---|---|
| F039 | Medium | **Regressed** | 2 bare catch blocks in VmService + ImportPveOvaCmdlet | | F039 | VmService.cs, ImportPveOvaCmdlet.cs | Medium | **Resolved** | Bare catch regression fixed. VmService:554 catches PveApiException; ImportPveOvaCmdlet:108 catches Exception+ThrowTerminatingError |
| F032 | Critical | Resolved | Inline task polling → WaitForTask | | F048 | src/PSProxmoxVE.Core/ | Medium | Wont_fix | ~216 sync-over-async via GetAwaiter().GetResult() — accepted pattern for PS 5.1 compat |
| F033 | Critical | Resolved | Guest exec polling → timeout added |
| F058 | Critical | Resolved | Container/storage polling → WaitForTask | **Note on JObject usage**: JObject/JArray/JToken are used extensively in services and cmdlets for internal JSON parsing (response deserialization). This is acceptable per D013 — the restriction is on **public API types** (model properties, OutputType), not internal implementation. No Newtonsoft types appear in model properties or OutputType attributes.
| F041 | Medium | Resolved | Unsealed cmdlets → all sealed |
| F037 | Medium | Resolved | Missing OutputType → all added | ### HttpClient Lifecycle (F045)
| F044 | Medium | Resolved | Dual JSON attributes → Newtonsoft only |
| F045 | Medium | Resolved | HttpClient per-call → DI support added | Resolved in prior scan. IPveHttpClient interface extracted; all 14 services accept shared client via constructor injection.
| F062 | Medium | Resolved | Restart-PveContainer ConfirmImpact → High |
| F063 | Medium | Resolved | Suspend-PveContainer ConfirmImpact → High |
--- ---
@@ -227,92 +157,84 @@ Two bare `catch` blocks violating D004 were found:
### Test Infrastructure ### Test Infrastructure
| Component | Framework | Location | Count | | Component | Framework | Target |
|---|---|---|---|
| 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 | | PSProxmoxVE.Core.Tests | xUnit 2.9.3 | net10.0, net48 |
| VMs | Yes | Yes | | PSProxmoxVE.Tests | Pester 5 | PowerShell 7.x |
| Containers | Yes | Yes | | Integration tests | Pester 5 | Live PVE 8 + PVE 9 |
| 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 ### Test Coverage by Area
- Pester tests use Describe/Context/It structure with descriptive names | Area | xUnit (Models/Services) | Pester (Unit) | Integration | Notes |
- Integration tests tagged with `@("Integration")` for filtering |---|---|---|---|---|
- Connection details injected via environment variables | Connection | — | Yes | Yes (00_Connection) | |
- Integration tests cover both PVE 8 and PVE 9 | Nodes | NodeModelTests | Yes | Yes (01_Nodes) | |
- xUnit tests use Moq for HTTP client mocking with realistic fixtures | Users | UserModelTests, UserServiceTests | Yes | Yes (02_Users) | |
- Test cleanup via AfterAll blocks | Storage | StorageModelTests, StorageServiceTests | Yes | Yes (03_Storage, 03a_Shared) | |
| Network | NetworkModelTests | Yes | Yes (04_Network) | |
| SDN | SdnModelTests | Yes | Yes (05_SDN) | |
| VMs | VmModelTests | Yes | Yes (06_VMs) | |
| Snapshots | SnapshotModelTests, SnapshotServiceTests | Yes | Yes (07_Snapshots) | |
| Templates | TemplateServiceTests | Yes | Yes (08_Templates) | |
| CloudInit | CloudInitServiceTests | Yes | Yes (09_CloudInit) | |
| Containers | ContainerModelTests | Yes | Yes (10_Containers) | |
| Firewall | FirewallModelTests | Yes | Yes (13_Firewall) | |
| Backup | BackupModelTests, BackupServiceTests | Yes | Yes (14_Backup) | |
| Tasks | TaskModelTests, TaskServiceTests | Yes | Yes (15_Tasks) | |
| Cluster | ClusterModelTests, ClusterServiceTests, ClusterConfigServiceTests | Yes | Yes (16_Cluster) | |
| HA | HaServiceTests | Yes (HaCmdlets.Tests.ps1) | Yes (16_Cluster) | Full CRUD for groups, rules, resources, status |
| Pools | PoolServiceTests | Yes (PoolCmdlets.Tests.ps1) | No | F046 — no integration tests |
### xUnit Test Stats
- 12 model test files, 12 service test files
- ~382 total xUnit tests (196 service tests added in recent remediation)
### Test Quality Assessment
- **Structure**: Arrange/Act/Assert pattern used consistently
- **Isolation**: Service tests use Moq-based IPveHttpClient mocks
- **Fixtures**: JSON fixture files for PVE 8 and 9 response deserialization
- **Integration**: 18 integration test files (0016 + 99_Cleanup) covering happy paths
- **Edge cases**: Model tests cover null/missing fields; service tests verify URL construction
### Finding F046: Integration Test Gaps
Still open. HA cmdlets now have integration coverage in 16_Cluster.Tests.ps1 (status, groups, rules, resources). Key areas still without integration tests: pool management, some newer SDN/firewall operations. Estimated 55-65 of ~172 cmdlets lack integration coverage.
--- ---
## Phase 4b — CI Integration Test Results ## Phase 4b — CI Integration Test Results
### Most Recent Completed Run
| Field | Value | | Field | Value |
|---|---| |---|---|
| Run ID | 23511293737 | | Workflow | integration-tests.yml |
| Conclusion | failure | | Latest completed run | 23571920298 |
| Created | 2026-03-24T20:42:58Z | | Conclusion | **success** |
| Head SHA | 0f3e2c1 | | Date | 2026-03-26T00:51:58Z |
| Branch | main | | Branch | main |
| SHA | d375695 |
**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. **Last integration run: PASSED** (both PVE 8 and PVE 9 matrices). No CI findings to generate.
**No test failures** — tests were skipped due to provision failure. A newer run (23596586594) is currently in_progress.
The most recent failure (23566610350, 2026-03-25) was a provisioning infrastructure issue (Docker/Terraform), not a test logic failure.
--- ---
## Phase 5 — Security Review ## Phase 5 — Security Review
| Finding ID | Area | Status | Notes | | Finding ID | Area | File | Severity | Status | Description |
|---|---|---|---| |---|---|---|---|---|---|
| | Credential handling | Pass | SecureString for all passwords, PSCredential for Connect | | F084 | Credential exposure | PveSession.cs, format.ps1xml | Medium | **Resolved** | format.ps1xml now hides Ticket/ApiToken/CsrfToken from default output |
| F084 | Session object exposure | **New** | PveSession exposes Ticket/ApiToken/CsrfToken in default pipeline output | | — | Credential handling | Cmdlets/ | — | Pass | All 7 password params use SecureString with Marshal try/finally |
| — | TLS/HTTPS | Pass | Enforced by default, SkipCertificateCheck opt-in with warning | | — | TLS/HTTPS | PveHttpClient.cs | — | Pass | HTTPS enforced; SkipCertificateCheck opt-in with WriteWarning |
| — | Input validation | Pass | Uri.EscapeDataString on all path params, ValidateRange on IDs | | — | URL encoding | Services/ | — | Pass | Uri.EscapeDataString on all dynamic path segments |
| — | Secret scanning | Pass | No secrets in committed files, .gitignore covers sensitive files | | — | Secret scanning | All files | — | Pass | No hardcoded credentials in tracked files; .env.test gitignored |
| — | Dependency security | Pass | All packages current, Dependabot configured | | — | Dependencies | .csproj files | — | Pass | Newtonsoft.Json 13.0.3, SharpCompress 0.38.0, PowerShellStandard.Library 5.1.1 |
### F084 — PveSession Exposes Auth Secrets in Pipeline Output (NEW) No new security findings this scan.
`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.
--- ---
@@ -320,87 +242,66 @@ Tests cover:
| Finding ID | Check | Pass/Fail | Notes | | Finding ID | Check | Pass/Fail | Notes |
|---|---|---|---| |---|---|---|---|
| — | RootModule | Pass | PSProxmoxVE.dll | | — | ModuleVersion | Pass | 0.1.0 |
| — | ModuleVersion | Pass | 0.1.0 (updated from tag by publish workflow) |
| — | GUID | Pass | a3f7c2d1-84e5-4b9f-a061-3e2d8c5f1a7b | | — | GUID | Pass | a3f7c2d1-84e5-4b9f-a061-3e2d8c5f1a7b |
| — | Author | Pass | goodolclint | | — | Author/CompanyName | Pass | goodolclint / Worklab |
| — | Description | Pass | Comprehensive description | | — | Description | Pass | Comprehensive, mentions PVE 8.x and 9.x |
| — | PowerShellVersion | Pass | 5.1 | | — | PowerShellVersion | Pass | 5.1 |
| — | CompatiblePSEditions | Pass | Desktop, Core | | — | CompatiblePSEditions | Pass | Desktop, Core |
| — | CmdletsToExport | Pass | 169 cmdlets explicitly listed | | — | Tags | Pass | 8 tags including Proxmox, PVE, IaC |
| — | Tags | Pass | 8 relevant tags | | — | LicenseUri | Pass | Points to LICENSE on main |
| — | LicenseUri | Pass | GitHub MIT license link | | — | ProjectUri | Pass | GitHub repo URL |
| — | ProjectUri | Pass | GitHub repo link | | F021 | IconUri | **Fail** | Missing — cosmetic only |
| — | ReleaseNotes | Pass | Present in PSData | | — | ReleaseNotes | Pass | Preview release description |
| F021 | IconUri | Fail | Not set — recommended for PSGallery listing | | — | CmdletsToExport | Pass | 172 cmdlets listed |
| — | Prerelease | Pass | 'preview' — appropriate for current state | | — | RequiredAssemblies | Pass | PSProxmoxVE.Core.dll, Newtonsoft.Json.dll |
| — | Framework target | Pass | netstandard2.0 for publishable projects | | — | FormatsToProcess | Pass | PSProxmoxVE.format.ps1xml |
| — | PS 5.1 smoke test | Pass | Verifies >= 150 commands load | | — | netstandard2.0 target | Pass | Both projects target only netstandard2.0 |
| — | MAML help | Pass | PSProxmoxVE.dll-Help.xml present | | — | Publish workflow | Pass | Tag-triggered, PS 5.1 smoke test, threshold >= 150 |
| — | DotNetFrameworkVersion | Pass | 4.8 |
| — | HelpInfoUri | Pass | Points to docs/cmdlets/ |
--- ---
## Phase 7 — Community & Repo Maintenance Standards ## Phase 7 — Community & Repo Maintenance
| Check | Pass/Fail | Notes | | Finding ID | 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 | | — | Issue templates | Pass | Bug report + feature request (YAML) + config.yml |
| D002 | SecureString passwords | Yes | grep `public string Password` — none found | | — | PR template | Pass | Structured checklist |
| D003 | Uri.EscapeDataString | Yes | 164 occurrences across 36 files | | — | CONTRIBUTING.md | Pass | .NET 10.0+ SDK, build/test, coding standards, PR process |
| D004 | No bare catches | **FAIL** | 2 bare catches found: VmService.cs:553, ImportPveOvaCmdlet.cs:277 → F039 regressed | | — | CODE_OF_CONDUCT.md | Pass | Contributor Covenant v2.1 |
| D005 | OutputType on all cmdlets | Yes | 169 cmdlets, 169 OutputType attributes | | — | SECURITY.md | Pass | Vulnerability disclosure with 48h response SLA |
| D006 | ConfirmImpact.High | Yes | All destructive cmdlets including Restart/Suspend-Container | | — | CODEOWNERS | Pass | Single maintainer |
| D007 | Sealed cmdlet classes | Yes | All 169 classes sealed | | — | LICENSE | Pass | MIT |
| D008 | Newtonsoft.Json only | Yes | No System.Text.Json references in src/ | | — | .editorconfig | Pass | C# and PS rules |
| D009 | netstandard2.0 targets | Yes | Both publishable projects target netstandard2.0 | | — | .gitattributes | Pass | Line ending normalization |
| D010 | ValidateRange on VmId | Yes | All VmId parameters have ValidateRange | | — | Branch protection | Pass | Documented in CLAUDE.md |
| D011 | Verb class constants | Yes | No string literal verbs found | | — | Commit conventions | Pass | Conventional commits |
| D012 | Magic string constants | Yes | ApiTokenPrefix, CsrfHeaderName constants in PveHttpClient | | — | DECISIONS.md | Pass | 13 active decisions, linked from CLAUDE.md |
| — | Dependabot | Pass | NuGet + GitHub Actions weekly |
| — | CHANGELOG | Pass | 0.1.0-preview entry |
| — | Release process | Pass | Tag-triggered publish.yml with GitHub Releases |
**1 regression detected**: F039 (bare catch blocks) — D004 violated in 2 locations. No new decisions needed. All community standards met. No findings.
--- ---
## Phase 9 — Prioritized Recommendations ## Phase 9 — Prioritized Recommendations
### Medium (5 findings) ### 🟡 Medium
| Finding ID | What | Where | Why | Fix | | 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)` | | F046 | Integration test coverage gaps | tests/Integration/ | ~55-65 cmdlets lack end-to-end integration tests | Add integration tests for pools, newer SDN/firewall operations |
| F084 | PveSession exposes auth secrets | PveSession.cs, format.ps1xml | Ticket/ApiToken/CsrfToken visible in pipeline output | Add format.ps1xml entry hiding secret properties | | F061 | PVE 9.0 endpoints partially covered (5/42) | — | 37 new PVE 9.0 endpoints unimplemented (SDN fabrics, bulk actions, OCI registry) | Prioritize SDN fabrics (14 endpoints) and bulk actions (6 endpoints) |
| 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) ### 🟢 Low
| Finding ID | What | Where | Why | Fix | | Finding ID | What | Where | Why | Fix |
|---|---|---|---|---| |---|---|---|---|---|
| F021 | No IconUri in manifest | PSProxmoxVE.psd1 | PSGallery listing lacks icon | Add IconUri pointing to project logo | | F021 | No IconUri in manifest PSData | PSProxmoxVE.psd1 | Cosmetic — improves PSGallery listing appearance | Add icon to repo and reference in manifest |
| F053 | HA subsystem 0% | src/ | 21 endpoints for HA management | Implement HA group/resource/rule cmdlets | | F054 | Ceph subsystem 0% coverage | — | 40 endpoints for OSD, MON, pools, status. Critical for hyperconverged setups | Implement Ceph cmdlets when demand warrants |
| F054 | Ceph subsystem 0% | src/ | 40 endpoints for hyperconverged | Implement Ceph pool/OSD/monitor cmdlets | | F067 | Disk management 0% coverage | — | 18 endpoints for LVM, ZFS, SMART. Needed for storage provisioning | Implement disk cmdlets |
| F067 | Disk management 0% | src/ | ZFS/LVM/directory ops missing | Implement disk management cmdlets | | F068 | Notifications 0% coverage | — | 32 cluster notification endpoints (PVE 8.1+) | Implement notification cmdlets |
| F068 | Notifications 0% | src/ | Notification targets not managed | Implement notification cmdlets | | F069 | ACME/Certificates 0% coverage | — | 23 combined endpoints for TLS certificate management | Implement ACME/cert cmdlets |
| F069 | ACME/Certificates 0% | src/ | Let's Encrypt + cert mgmt missing | Implement ACME/cert cmdlets |
+107 -24
View File
@@ -1,16 +1,16 @@
{ {
"_schema_version": "1.0", "_schema_version": "1.0",
"_description": "PSProxmoxVE stable findings ledger. IDs are permanent (F001, F002...). Resolved findings are never deleted — they are marked resolved with evidence. If a finding reappears, it is marked regressed and retains its original ID.", "_description": "PSProxmoxVE stable findings ledger. IDs are permanent (F001, F002...). Resolved findings are never deleted — they are marked resolved with evidence. If a finding reappears, it is marked regressed and retains its original ID.",
"last_updated": "2026-03-25", "last_updated": "2026-03-26",
"last_scan_date": "2026-03-25", "last_scan_date": "2026-03-26",
"counters": { "counters": {
"next_id": 86, "next_id": 86,
"total_open": 10, "total_open": 7,
"total_resolved": 73, "total_resolved": 77,
"total_regressed": 1, "total_regressed": 0,
"open": 10, "open": 7,
"resolved": 73, "resolved": 77,
"regressed": 1, "regressed": 0,
"wont_fix": 1 "wont_fix": 1
}, },
"findings": [ "findings": [
@@ -640,6 +640,11 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ]
}, },
@@ -1187,7 +1192,7 @@
"title": "Bare catch blocks in PveHttpClient, PveCmdletBase, VmService, ContainerService, GetPveVmCmdlet", "title": "Bare catch blocks in PveHttpClient, PveCmdletBase, VmService, ContainerService, GetPveVmCmdlet",
"category": "code_quality", "category": "code_quality",
"severity": "medium", "severity": "medium",
"status": "regressed", "status": "resolved",
"first_detected": "2026-03-21", "first_detected": "2026-03-21",
"files": [ "files": [
"src/PSProxmoxVE.Core/Services/VmService.cs", "src/PSProxmoxVE.Core/Services/VmService.cs",
@@ -1209,12 +1214,18 @@
"scan_date": "2026-03-24", "scan_date": "2026-03-24",
"local_id": null, "local_id": null,
"status": "regressed" "status": "regressed"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "fixed"
} }
], ],
"resolution": { "resolution": {
"scan_date": "2026-03-22", "scan_date": "2026-03-26",
"evidence": "All bare catches replaced with filtered catch (Exception ex) when (...) clauses that exclude OOM/SOE and are specific to PveApiException/HttpRequestException where appropriate", "report_id": "scan-9",
"verified_by": "self-reported" "evidence": "VmService.PingGuestAgent (line 554) now catches PveApiException specifically. ImportPveOvaCmdlet (line 108) catches Exception but calls ThrowTerminatingError (correct PS pattern). ImportPveOvaCmdlet line 279 catches PveApiException with StatusCode filter. grep for 'catch {' and 'catch (Exception)' with no filter finds zero bare catches in src/.",
"verified_by": "scan"
}, },
"regression_history": [ "regression_history": [
{ {
@@ -1452,6 +1463,11 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ]
}, },
@@ -1679,9 +1695,12 @@
"title": "HA subsystem 0% coverage", "title": "HA subsystem 0% coverage",
"category": "api_coverage", "category": "api_coverage",
"severity": "low", "severity": "low",
"status": "open", "status": "resolved",
"first_detected": "2026-03-21", "first_detected": "2026-03-21",
"files": [], "files": [
"src/PSProxmoxVE/Cmdlets/HA/",
"src/PSProxmoxVE.Core/Services/HaService.cs"
],
"description": "21 HA endpoints + 5 new PVE 9.0 endpoints. Essential for production clusters. Resources, groups, status, and rules.", "description": "21 HA endpoints + 5 new PVE 9.0 endpoints. Essential for production clusters. Resources, groups, status, and rules.",
"scan_history": [ "scan_history": [
{ {
@@ -1703,8 +1722,19 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "fixed"
} }
] ],
"resolution": {
"scan_date": "2026-03-26",
"report_id": "scan-9",
"verified_by": "scan",
"evidence": "14 HA cmdlets implemented: Get/New/Set/Remove-PveHaResource, Move-PveHaResource, Get/New/Set/Remove-PveHaGroup, Get-PveHaStatus, Get/New/Set/Remove-PveHaRule (PVE 9.0+). HaService.cs (493 lines) covers resources, groups, status, and rules endpoints."
}
}, },
{ {
"id": "F054", "id": "F054",
@@ -1735,6 +1765,11 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ]
}, },
@@ -1911,9 +1946,12 @@
"title": "Cluster config 0% coverage", "title": "Cluster config 0% coverage",
"category": "api_coverage", "category": "api_coverage",
"severity": "medium", "severity": "medium",
"status": "open", "status": "resolved",
"first_detected": "2026-03-22", "first_detected": "2026-03-22",
"files": [], "files": [
"src/PSProxmoxVE/Cmdlets/Cluster/",
"src/PSProxmoxVE.Core/Services/ClusterConfigService.cs"
],
"description": "10 endpoints for cluster create/join/node management. Fundamental for multi-node cluster automation.", "description": "10 endpoints for cluster create/join/node management. Fundamental for multi-node cluster automation.",
"scan_history": [ "scan_history": [
{ {
@@ -1930,18 +1968,31 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "fixed"
} }
] ],
"resolution": {
"scan_date": "2026-03-26",
"report_id": "scan-9",
"verified_by": "scan",
"evidence": "12 cluster cmdlets implemented: Get-PveClusterResource, Get-PveClusterStatus, Get-PveClusterNextId, Get/Set-PveClusterOption, Get-PveClusterConfig, Get/Add/Remove-PveClusterConfigNode, Get-PveClusterJoinInfo, Add-PveClusterMember, New-PveCluster. ClusterConfigService.cs (412 lines) covers create, join, node CRUD, and options."
}
}, },
{ {
"id": "F061", "id": "F061",
"title": "PVE 9.0 endpoints 0% coverage", "title": "PVE 9.0 endpoints partially covered (5/42)",
"category": "api_drift", "category": "api_drift",
"severity": "medium", "severity": "medium",
"status": "open", "status": "open",
"first_detected": "2026-03-22", "first_detected": "2026-03-22",
"files": [], "files": [
"description": "42 new PVE 9.0 endpoints available (HA rules, SDN fabrics, bulk actions, OCI registry). None implemented.", "src/PSProxmoxVE/Cmdlets/HA/"
],
"description": "42 new PVE 9.0 endpoints available. 5 HA rules endpoints are now implemented via Get/New/Set/Remove-PveHaRule. Remaining 37 unimplemented: SDN fabrics (14), bulk actions (6), SDN lock/rollback (3), node SDN views (8), OCI registry (2), LXC migrate GET (1), VM dbus-vmstate (1), node capabilities (2).",
"scan_history": [ "scan_history": [
{ {
"scan_date": "2026-03-22", "scan_date": "2026-03-22",
@@ -1957,8 +2008,14 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ],
"notes": "Improved from 0/42 to 5/42 (12%) with HA rules implementation. SDN fabrics (14 endpoints) are the next highest-value gap."
}, },
{ {
"id": "F062", "id": "F062",
@@ -2142,6 +2199,11 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ]
}, },
@@ -2169,6 +2231,11 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ]
}, },
@@ -2196,6 +2263,11 @@
"scan_date": "2026-03-23", "scan_date": "2026-03-23",
"local_id": null, "local_id": null,
"status": "open" "status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
} }
] ]
}, },
@@ -2682,7 +2754,7 @@
"title": "PveSession exposes auth secrets in default pipeline output", "title": "PveSession exposes auth secrets in default pipeline output",
"category": "security", "category": "security",
"severity": "medium", "severity": "medium",
"status": "open", "status": "resolved",
"first_detected": "2026-03-24", "first_detected": "2026-03-24",
"files": [ "files": [
"src/PSProxmoxVE.Core/Authentication/PveSession.cs", "src/PSProxmoxVE.Core/Authentication/PveSession.cs",
@@ -2694,8 +2766,19 @@
"scan_date": "2026-03-24", "scan_date": "2026-03-24",
"local_id": null, "local_id": null,
"status": "new" "status": "new"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "fixed"
} }
] ],
"resolution": {
"scan_date": "2026-03-26",
"report_id": "scan-9",
"verified_by": "scan",
"evidence": "PSProxmoxVE.format.ps1xml contains a PveSession TableControl view (line 1359-1408) that displays only Hostname, Port, AuthMode, Expired (via ScriptBlock), and Version. Sensitive properties (Ticket, ApiToken, CsrfToken) are hidden from default table output. Note: Format-List * still shows all public properties — this is inherent to PowerShell and cannot be prevented without making properties internal (which would break the HTTP client)."
}
}, },
{ {
"id": "F085", "id": "F085",