Files
Clint Branham 56dcf22cea fix: address PR #69 review feedback
- VmService.ExecuteGuestCommand: guard against null elements in -Args.
  The old JSON-serialization tolerated nulls (as "null"); the repeated-key
  path would NRE in EncodeFormValue. Throw a clear ArgumentException instead.
- findings.json: refresh the stale counters block (untouched since F085) to
  the actual ledger state — next_id 92, resolved 83 — and bump last_updated
  to 2026-05-22. last_scan_date stays 2026-03-26 (F086–F091 came from issue
  triage, not a formal review scan).
- VmServiceTests: add empty-array (single command entry) and null-element
  (throws) cases.

Note: F091 is the correct next ID — F086–F090 already exist from prior
merged PRs (#60/#61/#66/#67); only the counters were lagging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:38:54 -05:00

3009 lines
99 KiB
JSON

{
"_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.",
"last_updated": "2026-05-22",
"last_scan_date": "2026-03-26",
"counters": {
"next_id": 92,
"total_open": 7,
"total_resolved": 83,
"total_regressed": 0,
"open": 7,
"resolved": 83,
"regressed": 0,
"wont_fix": 1
},
"findings": [
{
"id": "F001",
"title": "No PSGallery publish workflow",
"category": "psgallery",
"severity": "critical",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
".github/workflows/"
],
"description": "No CI/CD workflow for publishing to PSGallery. Cannot automate releases.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "C1",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "publish.yml workflow added, triggered on tag push with Publish-Module and GitHub Release creation",
"verified_by": "scan"
}
},
{
"id": "F002",
"title": "README says 'not PSGallery'",
"category": "psgallery",
"severity": "critical",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"README.md"
],
"description": "README installation section says module is not on PSGallery, contradicting publication goal.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "C2",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "README updated with Install-Module PSProxmoxVE instructions",
"verified_by": "scan"
}
},
{
"id": "F003",
"title": "No ReleaseNotes in manifest PSData",
"category": "psgallery",
"severity": "critical",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/PSProxmoxVE.psd1"
],
"description": "PSGallery strongly recommends ReleaseNotes in module manifest PSData section.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "C3",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "ReleaseNotes added to PSData section of manifest",
"verified_by": "scan"
}
},
{
"id": "F004",
"title": "No HelpMessage on any cmdlet parameter",
"category": "code_quality",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "Get-Help shows no parameter descriptions because HelpMessage attribute is missing on all [Parameter] attributes.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H1",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "HelpMessage present on virtually all parameters across all 83 cmdlets",
"verified_by": "scan"
}
},
{
"id": "F005",
"title": "No WriteVerbose in cmdlets except Connection",
"category": "code_quality",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "Users cannot see which API calls are being made. Only Connection cmdlets use WriteVerbose.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H2",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "WriteVerbose used extensively throughout all cmdlets",
"verified_by": "scan"
}
},
{
"id": "F006",
"title": "Missing CONTRIBUTING.md",
"category": "community",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"CONTRIBUTING.md"
],
"description": "No contributor guide with dev setup, coding standards, test instructions, or PR process.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H3",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "CONTRIBUTING.md created with prerequisites, building, tests, coding standards, PR process",
"verified_by": "scan"
}
},
{
"id": "F007",
"title": "Missing SECURITY.md",
"category": "community",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"SECURITY.md"
],
"description": "No vulnerability disclosure policy.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H4",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "SECURITY.md created with vulnerability disclosure policy and 48h response SLA",
"verified_by": "scan"
}
},
{
"id": "F008",
"title": "No WriteWarning on -SkipCertificateCheck",
"category": "security",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Connection/ConnectPveServerCmdlet.cs"
],
"description": "Users may not realize they are disabling TLS certificate verification.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H5",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "WriteWarning emitted with man-in-the-middle advisory when SkipCertificateCheck is used",
"verified_by": "scan"
}
},
{
"id": "F009",
"title": "Reset-PveVm uses hardcoded verb string literal",
"category": "code_quality",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/ResetPveVmCmdlet.cs"
],
"description": "Reset-PveVm uses [Cmdlet(\"Reset\", ...)] instead of VerbsCommon.Reset, inconsistent with all other cmdlets.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H6",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "Now uses VerbsCommon.Reset constant",
"verified_by": "scan"
},
"decisions_ref": "D012"
},
{
"id": "F010",
"title": "Missing GitHub issue and PR templates",
"category": "community",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
".github/ISSUE_TEMPLATE/",
".github/pull_request_template.md"
],
"description": "No structured issue reporting or PR process templates.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "H8",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "Bug report (YAML), feature request (YAML), and PR template added",
"verified_by": "scan"
}
},
{
"id": "F011",
"title": "Stop-PveVm and Reset-PveVm lack ConfirmImpact.High",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/StopPveVmCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Vms/ResetPveVmCmdlet.cs"
],
"description": "Stopping or resetting VMs can cause data loss but these cmdlets don't prompt for confirmation by default.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M1",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "ConfirmImpact.High added to both Stop-PveVm and Reset-PveVm",
"verified_by": "scan"
},
"decisions_ref": "D007"
},
{
"id": "F012",
"title": "No ValidateRange on VmId parameters",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "PVE requires VMID 100-999999999 but no ValidateRange attribute enforces this.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M2",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "ValidateRange(100, 999999999) added to all VmId parameters",
"verified_by": "scan"
},
"decisions_ref": "D011"
},
{
"id": "F013",
"title": "Container snapshots not supported",
"category": "api_coverage",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Containers/"
],
"description": "Snapshots are only implemented for VMs, not LXC containers, despite PVE supporting both.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M3",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "4 container snapshot cmdlets added: Get/New/Remove/Restore-PveContainerSnapshot",
"verified_by": "scan"
}
},
{
"id": "F014",
"title": "No integration tests for network modifications",
"category": "testing",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"tests/PSProxmoxVE.Tests/Integration/"
],
"description": "Network CRUD (create/modify/delete/apply) is tested only at Pester level, not end-to-end.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M4",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "Integration tests added for network create/modify/delete/apply with revert",
"verified_by": "scan"
}
},
{
"id": "F015",
"title": "No CODE_OF_CONDUCT.md",
"category": "community",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"CODE_OF_CONDUCT.md"
],
"description": "Expected for open-source projects to establish community standards.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M5",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "Contributor Covenant v2.1 adopted",
"verified_by": "scan"
}
},
{
"id": "F016",
"title": "No .gitattributes file",
"category": "community",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
".gitattributes"
],
"description": "Line ending consistency across platforms not enforced.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M6",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": ".gitattributes added with line ending normalization, csharp diff, binary markers",
"verified_by": "scan"
}
},
{
"id": "F017",
"title": "CS1591 suppressed in Core project",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE.Core/PSProxmoxVE.Core.csproj"
],
"description": "Public API lacks XML documentation because CS1591 warning is suppressed.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M7",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "CS1591 no longer suppressed, XML doc comments added to all public types",
"verified_by": "scan"
}
},
{
"id": "F018",
"title": "No badges in README",
"category": "psgallery",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"README.md"
],
"description": "No build status, PSGallery version, or license badges to improve discoverability and trust.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M8",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "Build, Unit Tests, License badges added to README",
"verified_by": "scan"
}
},
{
"id": "F019",
"title": "CHANGELOG has only [Unreleased] section",
"category": "psgallery",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"CHANGELOG.md"
],
"description": "No versioned release entries in CHANGELOG, no release history.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M9",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "0.1.0-preview versioned entry added to CHANGELOG",
"verified_by": "scan"
}
},
{
"id": "F020",
"title": "Invoke-PveVmGuestExec lacks ShouldProcess",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/InvokePveVmGuestExecCmdlet.cs"
],
"description": "Executes commands inside a VM (a mutating operation) but does not implement SupportsShouldProcess.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "M10",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "SupportsShouldProcess added; all destructive/mutating cmdlets now implement it",
"verified_by": "scan"
}
},
{
"id": "F021",
"title": "No IconUri in manifest PSData",
"category": "psgallery",
"severity": "low",
"status": "open",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/PSProxmoxVE.psd1"
],
"description": "Improves PSGallery listing appearance. Cosmetic only.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L1",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": "L1",
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": "L1",
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": "L1",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
}
]
},
{
"id": "F022",
"title": "No online help (MAML/platyPS)",
"category": "psgallery",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/"
],
"description": "Get-Help -Online does not work. No MAML help file for cmdlet documentation.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L2",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "MAML help file (PSProxmoxVE.dll-Help.xml) present with 75 cmdlet documentation files in docs/cmdlets/",
"verified_by": "scan"
}
},
{
"id": "F023",
"title": "No firewall cmdlets",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Firewall/"
],
"description": "No cmdlets for creating/managing firewall rules at cluster, node, or VM level. Critical gap for security automation.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L3",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": "H1",
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "21 cluster-level firewall cmdlets added covering rules, groups, aliases, IP sets, options, and refs",
"verified_by": "self-reported"
}
},
{
"id": "F024",
"title": "No backup/vzdump cmdlets",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Backup/"
],
"description": "Essential for DR automation. No cmdlets for creating backups, managing schedules, or restoring.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L4",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": "H2",
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "6 backup cmdlets added: New-PveBackup, Get/New/Set/Remove-PveBackupJob, Get-PveBackupInfo",
"verified_by": "self-reported"
}
},
{
"id": "F025",
"title": "No pool management cmdlets",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Pools/"
],
"description": "Required for multi-tenant environments. No cmdlets for creating/managing resource pools.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L5",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": "M2",
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "4 pool cmdlets added: Get/New/Set/Remove-PvePool",
"verified_by": "self-reported"
}
},
{
"id": "F026",
"title": "new Random() used for multipart boundary generation",
"category": "code_quality",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE.Core/Client/PveHttpClient.cs"
],
"description": "Not cryptographically secure. Low risk for multipart boundaries but could use RandomNumberGenerator.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L6",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "RandomNumberGenerator now used for boundary generation",
"verified_by": "scan"
}
},
{
"id": "F027",
"title": "No alias support in module",
"category": "psgallery",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/PSProxmoxVE.psd1"
],
"description": "Some users prefer short aliases for frequently used cmdlets.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L7",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "7 aliases defined in module manifest AliasesToExport",
"verified_by": "scan"
}
},
{
"id": "F028",
"title": "No tagged releases or GitHub Releases",
"category": "psgallery",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
".github/workflows/publish.yml"
],
"description": "No GitHub Releases configured, no release workflow, no tagged releases in git history.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L8",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "publish.yml creates GitHub Releases via softprops/action-gh-release on tag push",
"verified_by": "scan"
}
},
{
"id": "F029",
"title": "No container migration cmdlet",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Containers/"
],
"description": "VMs have Move-PveVm for migration but containers have no equivalent.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L9",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "Move-PveContainer cmdlet added for container migration",
"verified_by": "scan"
}
},
{
"id": "F030",
"title": "No SDN subnet management cmdlets",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
"src/PSProxmoxVE/Cmdlets/Network/"
],
"description": "SDN zones and VNets exist but subnets are missing.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": "L10",
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-21",
"evidence": "3 SDN subnet cmdlets added: Get/New/Remove-PveSdnSubnet",
"verified_by": "scan"
}
},
{
"id": "F031",
"title": "Hardcoded test password in CI workflow and Terraform",
"category": "security",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-19",
"files": [
".github/workflows/integration-tests.yml",
"tests/infrastructure/variables.tf"
],
"description": "Hardcoded test password in integration-tests.yml env var and variables.tf default. Password moved to GitHub Actions secret (PVE_TEST_PASSWORD) and Terraform default removed. Integration README examples updated to use placeholders.",
"scan_history": [
{
"scan_date": "2026-03-19",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-21",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": "S1/S2",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-7",
"verified_by": "scan",
"evidence": "integration-tests.yml now uses ${{ secrets.PVE_TEST_PASSWORD }}. variables.tf default removed (requires TF_VAR_test_vm_password env var). README examples use <your-test-password> placeholder. No hardcoded passwords remain in tracked files."
}
},
{
"id": "F032",
"title": "Infinite loop task-polling in VM snapshot and network cmdlets",
"category": "code_quality",
"severity": "critical",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Network/InvokePveNetworkApplyCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Snapshots/NewPveSnapshotCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Snapshots/RestorePveSnapshotCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Snapshots/RemovePveSnapshotCmdlet.cs"
],
"description": "while(true) with no timeout in InvokePveNetworkApply, NewPveSnapshot, RestorePveSnapshot, RemovePveSnapshot private WaitForTask methods. Cmdlets hang forever if task stalls.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ1-CQ4",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "All four cmdlets now use TaskService.WaitForTask with timeout enforcement, failure detection, and WriteProgress support",
"verified_by": "self-reported"
},
"decisions_ref": "D001"
},
{
"id": "F033",
"title": "Infinite poll loop in InvokePveVmGuestExec",
"category": "code_quality",
"severity": "critical",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/InvokePveVmGuestExecCmdlet.cs"
],
"description": "while(true) polling exec-status with no timeout. Cmdlet hangs forever if guest exec never completes.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ5",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "Added Timeout parameter with Stopwatch + TimeoutException",
"verified_by": "self-reported"
},
"decisions_ref": "D001"
},
{
"id": "F034",
"title": "RemovePveRoleCmdlet missing ConfirmImpact.High",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Users/RemovePveRoleCmdlet.cs"
],
"description": "Deleting roles is a significant, potentially disruptive operation but does not prompt for confirmation.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ17/M1",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "ConfirmImpact = ConfirmImpact.High added",
"verified_by": "self-reported"
},
"decisions_ref": "D007"
},
{
"id": "F035",
"title": "Pool/CephPool parameter conflict in NewPveStorageCmdlet",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Storage/NewPveStorageCmdlet.cs"
],
"description": "New-PveStorage allows specifying both Pool and CephPool parameters simultaneously, which is invalid.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ6",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "Runtime validation added with ThrowTerminatingError if both Pool and CephPool specified",
"verified_by": "self-reported"
}
},
{
"id": "F036",
"title": "Duplicated WaitForTask methods across cmdlets",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "4 VM/network cmdlets had copy-pasted private WaitForTask implementations instead of using the shared TaskService.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ7",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "All cmdlets now use TaskService.WaitForTask",
"verified_by": "self-reported"
},
"decisions_ref": "D001"
},
{
"id": "F037",
"title": "~54 cmdlets missing OutputType attribute",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "Approximately 54 cmdlets lack [OutputType] attribute, degrading IntelliSense and pipeline type inference.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ9",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "All 169 cmdlets now have [OutputType] attributes",
"verified_by": "self-reported"
},
"decisions_ref": "D006"
},
{
"id": "F038",
"title": "Firewall VmId defaults to 0 instead of nullable",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Firewall/"
],
"description": "Firewall cmdlet VmId parameter uses int with default 0 instead of nullable int, making it impossible to distinguish unset from VM 0.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ10",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "VmId changed to nullable int pattern",
"verified_by": "self-reported"
},
"decisions_ref": "D011"
},
{
"id": "F039",
"title": "Bare catch blocks in PveHttpClient, PveCmdletBase, VmService, ContainerService, GetPveVmCmdlet",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE.Core/Services/VmService.cs",
"src/PSProxmoxVE/Cmdlets/Vms/ImportPveOvaCmdlet.cs"
],
"description": "Two bare catch {} blocks found: VmService.PingGuestAgent (line 553) and ImportPveOvaCmdlet (line 277). Both swallow all exceptions including OutOfMemoryException/StackOverflowException.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ11-CQ14",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
},
{
"scan_date": "2026-03-24",
"local_id": null,
"status": "regressed"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-26",
"report_id": "scan-9",
"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": [
{
"regressed_in_scan": "2026-03-24",
"description": "Bare catch blocks reintroduced in VmService.PingGuestAgent (line 553) and ImportPveOvaCmdlet VM retrieval fallback (line 277). Both should use filtered catch per D004."
}
],
"decisions_ref": "D004"
},
{
"id": "F040",
"title": "Broad exception catches in status polling",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/PveCmdletBase.cs",
"src/PSProxmoxVE.Core/Services/VmService.cs",
"src/PSProxmoxVE.Core/Services/ContainerService.cs"
],
"description": "Filtered catch(Exception ex) when(...) clauses are better than bare catches but still catch broadly. Could be more specific exception types.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ8",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "CQ8/M6",
"status": "open"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"report_id": "scan-4",
"verified_by": "scan",
"evidence": "PveCmdletBase.cs:149 uses 'when (ex is not OutOfMemoryException and not StackOverflowException)'. ContainerService.cs:44 and VmService.cs:48 filter to 'PveApiException or HttpRequestException'. All catch(Exception) blocks in cmdlets call ThrowTerminatingError (correct PS pattern). No bare catches found anywhere in src/."
}
},
{
"id": "F041",
"title": "~95 cmdlets not sealed",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "Approximately 95 cmdlet classes are not sealed, allowing unintended inheritance.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ18",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "All 169 cmdlets are now sealed",
"verified_by": "self-reported"
},
"decisions_ref": "D008"
},
{
"id": "F042",
"title": "SuspendPveVmCmdlet missing ConfirmImpact.High",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/SuspendPveVmCmdlet.cs"
],
"description": "Suspending a VM can cause issues but does not prompt for confirmation.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ19",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "ConfirmImpact.High added to SuspendPveVmCmdlet",
"verified_by": "self-reported"
},
"decisions_ref": "D007"
},
{
"id": "F043",
"title": "RestartPveVmCmdlet missing ConfirmImpact declaration",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/RestartPveVmCmdlet.cs"
],
"description": "Restarting a VM is disruptive but does not set ConfirmImpact.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ20",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "ConfirmImpact.High added to RestartPveVmCmdlet",
"verified_by": "self-reported"
},
"decisions_ref": "D007"
},
{
"id": "F044",
"title": "Dual JSON serialization attributes (JsonProperty + JsonPropertyName)",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE.Core/Models/"
],
"description": "Model classes have both Newtonsoft [JsonProperty] and System.Text.Json [JsonPropertyName] attributes. Only Newtonsoft is used at runtime.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ21",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "[JsonPropertyName] attributes removed, only [JsonProperty] (Newtonsoft) remains",
"verified_by": "self-reported"
},
"decisions_ref": "D009"
},
{
"id": "F045",
"title": "HttpClient per-call pattern bypasses connection pooling",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/"
],
"description": "38 cmdlets create new PveHttpClient per operation instead of going through services, bypassing connection pooling.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ11",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "CQ11/M1",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "IPveHttpClient interface extracted to Client/IPveHttpClient.cs. All 14 services now accept IPveHttpClient via constructor injection, enabling shared client instances. PveHttpClient implements IPveHttpClient."
}
},
{
"id": "F046",
"title": "Significant integration test coverage gaps",
"category": "testing",
"severity": "medium",
"status": "open",
"first_detected": "2026-03-21",
"files": [
"tests/PSProxmoxVE.Tests/Integration/"
],
"description": "Many cmdlets lack end-to-end integration tests. 64 of 169 cmdlets untested in integration as of latest scan.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "M3/M4",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "M2",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
}
]
},
{
"id": "F047",
"title": "Publishable projects multi-target beyond netstandard2.0",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/PSProxmoxVE.csproj",
"src/PSProxmoxVE.Core/PSProxmoxVE.Core.csproj"
],
"description": "Both publishable projects (PSProxmoxVE.csproj, PSProxmoxVE.Core.csproj) target netstandard2.0;net10.0;net48 but D009 requires publishable projects to target only netstandard2.0. Multi-targeting inflates the published module and may ship framework-specific assemblies unnecessarily.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ6",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "CQ6/H3",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"decisions_ref": "D010",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "PSProxmoxVE.csproj and PSProxmoxVE.Core.csproj now target <TargetFramework>netstandard2.0</TargetFramework> only. All #if NET48/NETSTANDARD2_0 conditionals removed from PveHttpClient.cs. build.yml restructured. Test project retains net10.0+net48."
}
},
{
"id": "F048",
"title": "Sync-over-async via GetAwaiter().GetResult()",
"category": "code_quality",
"severity": "medium",
"status": "wont_fix",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE.Core/"
],
"description": "~216 call sites use .GetAwaiter().GetResult(). Standard for PS binary modules but carries theoretical deadlock risk. Accepted tradeoff for PS 5.1 compatibility.",
"notes": "Accepted tradeoff: PowerShell binary modules targeting netstandard2.0 for PS 5.1 Desktop compatibility have no async pipeline. .GetAwaiter().GetResult() is the standard pattern used by all major PS binary modules. No deadlock risk in practice because PveHttpClient uses ConfigureAwait(false).",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ7",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "CQ7",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "wont_fix"
}
]
},
{
"id": "F049",
"title": "Auth header names are magic strings",
"category": "code_quality",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE.Core/Client/PveHttpClient.cs"
],
"description": "Auth header names (PVEAPIToken=, CSRFPreventionToken) are inline string literals instead of named constants.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "CQ2/CQ22",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "Extracted to const string ApiTokenPrefix and CsrfHeaderName fields",
"verified_by": "self-reported"
},
"decisions_ref": "D012"
},
{
"id": "F050",
"title": "URL-encode snapshot/node names in API paths",
"category": "security",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE.Core/Services/NodeService.cs",
"src/PSProxmoxVE.Core/Services/NetworkService.cs",
"src/PSProxmoxVE.Core/Services/CloudInitService.cs",
"src/PSProxmoxVE.Core/Services/TaskService.cs",
"src/PSProxmoxVE.Core/Services/TemplateService.cs"
],
"description": "Uri.EscapeDataString() missing on node, iface, zone, and vnet path parameters in 5 service files (20 instances total). NodeService (7 methods), NetworkService (7 methods incl. RemoveSdnZone/RemoveSdnVnet), CloudInitService (3 methods), TaskService (2 methods), TemplateService (1 method). Other services (VmService, ContainerService, SnapshotService, FirewallService, PoolService, UserService, StorageService) are properly escaped.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "S1/M5",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "regressed"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-7",
"verified_by": "scan",
"evidence": "Uri.EscapeDataString() added to all 20 instances across NodeService (7), NetworkService (7), CloudInitService (3), TaskService (2), TemplateService (1). Verified via grep: no unescaped {node}/{iface}/{zone}/{vnet} remain in any service file URL paths."
},
"decisions_ref": "D003",
"notes": "Previously resolved in scan-4 (2026-03-22) with evidence \"Uri.EscapeDataString() now used consistently across all services\". Regression found in scan-7: 5 of 14 services still lack encoding on node/iface/zone/vnet params.",
"regression_history": [
{
"regressed_in_scan": "2026-03-23",
"report_id": "scan-7",
"description": "20 instances of unescaped path params found in NodeService (7), NetworkService (7), CloudInitService (3), TaskService (2), TemplateService (1). Prior resolution evidence was self-reported, not scan-verified."
}
]
},
{
"id": "F051",
"title": "SetPveVmGuestPassword uses plain string not SecureString",
"category": "security",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/SetPveVmGuestPasswordCmdlet.cs"
],
"description": "Guest password parameter is a plain string instead of SecureString, inconsistent with Connect-PveServer's credential handling.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "S1",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "Now uses SecureString with Marshal.SecureStringToGlobalAllocUnicode + ZeroFreeGlobalAllocUnicode in try/finally",
"verified_by": "self-reported"
},
"decisions_ref": "D002"
},
{
"id": "F052",
"title": "Debug script contains hardcoded API token and IP",
"category": "security",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"debug/Capture-UploadDiff.ps1"
],
"description": "debug/Capture-UploadDiff.ps1 contained a hardcoded API token and PVE host IP.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "S2",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "Replaced with placeholder tokens: <PVE_HOST>, <USER>@<REALM>!<TOKENID>=<TOKEN_UUID>",
"verified_by": "self-reported"
}
},
{
"id": "F053",
"title": "HA subsystem 0% coverage",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"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.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "L3",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "H1",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"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",
"title": "Ceph subsystem 0% coverage",
"category": "api_coverage",
"severity": "low",
"status": "open",
"first_detected": "2026-03-21",
"files": [],
"description": "40 endpoints for OSD, MON, pool, status operations. Critical for hyperconverged infrastructure.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "L3",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "H2",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
}
]
},
{
"id": "F055",
"title": "Access groups and domains cmdlets missing",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Users/"
],
"description": "No cmdlets for /access/groups or /access/domains CRUD. Useful for LDAP/AD integration.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "L4",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "8 cmdlets added: Get/New/Set/Remove-PveGroup and Get/New/Set/Remove-PveDomain plus Set-PvePassword",
"verified_by": "self-reported"
}
},
{
"id": "F056",
"title": "PSGallery version badge missing",
"category": "psgallery",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"README.md"
],
"description": "README should show PSGallery version badge after first publish.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "L5",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "PSGallery badge present in README badges",
"verified_by": "self-reported"
}
},
{
"id": "F057",
"title": "SDN IPAM/DNS/Controller cmdlets missing",
"category": "api_coverage",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-21",
"files": [
"src/PSProxmoxVE/Cmdlets/Network/"
],
"description": "SDN zones/VNets/subnets covered but IPAM, DNS, and controller management missing.",
"scan_history": [
{
"scan_date": "2026-03-21",
"local_id": "L6",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-22",
"evidence": "12 cmdlets added: Get/New/Remove/Set for SdnIpam, SdnDns, SdnController plus Invoke-PveSdnApply",
"verified_by": "self-reported"
}
},
{
"id": "F058",
"title": "Infinite loop task-polling in container snapshot and storage cmdlets",
"category": "code_quality",
"severity": "critical",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE/Cmdlets/Containers/NewPveContainerSnapshotCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Containers/RemovePveContainerSnapshotCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Containers/RestorePveContainerSnapshotCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Storage/InvokePveStorageDownloadCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Storage/SendPveFileCmdlet.cs"
],
"description": "while(true) with no timeout in NewPveContainerSnapshot, RemovePveContainerSnapshot, RestorePveContainerSnapshot, InvokePveStorageDownload, and SendPveFile. Same anti-pattern that was fixed in VM snapshot cmdlets. Cmdlets hang forever if task stalls.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CQ1-CQ5/C1",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "C1",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"decisions_ref": "D001",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "All 5 cmdlets now use TaskService.WaitForTask (NewPveContainerSnapshotCmdlet.cs:67, RemovePveContainerSnapshotCmdlet.cs:59, RestorePveContainerSnapshotCmdlet.cs:62, InvokePveStorageDownloadCmdlet.cs:74, SendPveFileCmdlet.cs:134). No while(true) task-polling loops remain in any of these files."
}
},
{
"id": "F059",
"title": "VM/CT-level firewall 0% coverage",
"category": "api_coverage",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE.Core/Services/FirewallService.cs",
"src/PSProxmoxVE/Cmdlets/Firewall/"
],
"description": "~44 combined endpoints for per-VM and per-CT firewall rules. Cluster-level is covered (21 cmdlets) but VM/CT-level is not.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "M3",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-24",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-24",
"report_id": "scan-8",
"verified_by": "scan",
"evidence": "All 17 firewall cmdlets support a Level parameter (Cluster/Node/Vm/Container). FirewallService.cs line 12 documents scope support. SetPveFirewallRuleCmdlet accepts Level=Vm/Container with Node and VmId params. Same pattern in all firewall cmdlets (rules, aliases, ipsets, options, refs)."
}
},
{
"id": "F060",
"title": "Cluster config 0% coverage",
"category": "api_coverage",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"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.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "M4",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"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",
"title": "PVE 9.0 endpoints partially covered (5/42)",
"category": "api_drift",
"severity": "medium",
"status": "open",
"first_detected": "2026-03-22",
"files": [
"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_date": "2026-03-22",
"local_id": "M5",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"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",
"title": "RestartPveContainerCmdlet missing ConfirmImpact.High",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE/Cmdlets/Containers/RestartPveContainerCmdlet.cs"
],
"description": "RestartPveVmCmdlet has ConfirmImpact.High but the container counterpart does not. Inconsistent.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CQ9",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"decisions_ref": "D007",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "RestartPveContainerCmdlet.cs:15 now has ConfirmImpact = ConfirmImpact.High, matching RestartPveVmCmdlet."
}
},
{
"id": "F063",
"title": "SuspendPveContainerCmdlet missing ConfirmImpact.High",
"category": "code_quality",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE/Cmdlets/Containers/SuspendPveContainerCmdlet.cs"
],
"description": "SuspendPveVmCmdlet has ConfirmImpact.High but the container counterpart does not. Inconsistent.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CQ10",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"decisions_ref": "D007",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "SuspendPveContainerCmdlet.cs:14 now has ConfirmImpact = ConfirmImpact.High, matching SuspendPveVmCmdlet."
}
},
{
"id": "F064",
"title": "System.Management.Automation pinned to 7.4.0",
"category": "code_quality",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE/PSProxmoxVE.csproj"
],
"description": "Consider updating when migrating to .NET 10 target framework.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CQ12/L2",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"decisions_ref": "D010",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "net10.0 ItemGroup with System.Management.Automation 7.5.0 removed from PSProxmoxVE.csproj as part of F047 netstandard2.0-only migration. Publishable project now uses PowerShellStandard.Library 5.1.1 unconditionally."
}
},
{
"id": "F065",
"title": "No config.yml for issue templates",
"category": "community",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
".github/ISSUE_TEMPLATE/"
],
"description": "No .github/ISSUE_TEMPLATE/config.yml to control blank issue creation.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "L3",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": ".github/ISSUE_TEMPLATE/config.yml exists with blank_issues_enabled: false and contact_links to Discussions."
}
},
{
"id": "F066",
"title": "No CODEOWNERS file",
"category": "community",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"CODEOWNERS"
],
"description": "Documents ownership for PR auto-assignment. Low priority for single-maintainer project.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "L4",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "CODEOWNERS file exists (15 bytes) in repo root."
}
},
{
"id": "F067",
"title": "Disk management 0% coverage",
"category": "api_coverage",
"severity": "low",
"status": "open",
"first_detected": "2026-03-22",
"files": [],
"description": "18 endpoints for LVM, ZFS, SMART, wipe operations. Needed for storage provisioning.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "L5",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
}
]
},
{
"id": "F068",
"title": "Notifications subsystem 0% coverage",
"category": "api_coverage",
"severity": "low",
"status": "open",
"first_detected": "2026-03-22",
"files": [],
"description": "32 cluster notification endpoints. New in PVE 8.1+, essential for monitoring/alerting automation.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "L6",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
}
]
},
{
"id": "F069",
"title": "ACME/Certificates 0% coverage",
"category": "api_coverage",
"severity": "low",
"status": "open",
"first_detected": "2026-03-22",
"files": [],
"description": "23 combined endpoints for TLS certificate management and ACME.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "L7",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-26",
"local_id": null,
"status": "open"
}
]
},
{
"id": "F070",
"title": "Verify netstandard2.0 loads on Windows PowerShell 5.1",
"category": "psgallery",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
".github/workflows/publish.yml"
],
"description": "Publish workflow builds only netstandard2.0 but manifest declares Desktop compatible with DotNetFrameworkVersion 4.8. Should add PS 5.1 smoke test.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "L8",
"status": "new"
},
{
"scan_date": "2026-03-22",
"local_id": "L8",
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "publish.yml now includes smoke-test-ps51 job on windows-latest that imports the netstandard2.0 module in Windows PowerShell 5.1 (shell: powershell) and verifies command count >= 150. Publish job depends on smoke test passing."
}
},
{
"id": "F071",
"title": "Missing Uri.EscapeDataString in cmdlet URL constructions",
"category": "security",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE/Cmdlets/Templates/GetPveTemplateCmdlet.cs"
],
"description": "GetPveTemplateCmdlet.cs:52 uses nodes/{node}/qemu without Uri.EscapeDataString on the node variable. All other previously cited cmdlets (~14) have been fixed. 1 instance remains.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CQ-NEW1",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"decisions_ref": "D003",
"notes": "Reduced from ~15 files to 1 in scan-5. All network, snapshot, container snapshot, storage, and CloudInit cmdlets now use EscapeDataString.",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "GetPveTemplateCmdlet.cs:53 now uses Uri.EscapeDataString(node) in the API path. Added using System; directive."
}
},
{
"id": "F072",
"title": "Unnecessary System.Text.Json dependency in Core.csproj",
"category": "code_quality",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"src/PSProxmoxVE.Core/PSProxmoxVE.Core.csproj"
],
"description": "System.Text.Json 8.0.5 referenced for netstandard2.0/net48 targets despite D008 Newtonsoft-only policy. No [JsonPropertyName] attributes or System.Text.Json namespaces in source. Dependency appears unused and adds to assembly surface area.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CQ-NEW2",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"decisions_ref": "D008",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "System.Text.Json no longer referenced in PSProxmoxVE.Core.csproj. grep -c System.Text.Json returns 0."
}
},
{
"id": "F073",
"title": "build.yml references net9.0 but test project targets net10.0",
"category": "code_quality",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
".github/workflows/build.yml",
"tests/PSProxmoxVE.Core.Tests/PSProxmoxVE.Core.Tests.csproj"
],
"description": "build.yml workflow builds and tests with --framework net9.0 but the test project now targets net10.0;net48. The workflow will fail because the test csproj no longer has a net9.0 target.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "PG-NEW1",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"decisions_ref": "D009",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "build.yml now references --framework net10.0 at lines 51-53. No net9.0 references remain in build.yml."
}
},
{
"id": "F074",
"title": "Publish workflow smoke test threshold too low",
"category": "psgallery",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
".github/workflows/publish.yml"
],
"description": "Publish workflow checks module has >= 60 commands but module exports ~169 cmdlets. Threshold should be ~150 to catch regressions that drop significant cmdlets.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "PG-NEW2",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "publish.yml:58 checks commands.Count -lt 150 (was 60). With 169 exported cmdlets, threshold of 150 catches regressions while allowing minor variance."
}
},
{
"id": "F075",
"title": "~88 cmdlets lack markdown help documentation",
"category": "psgallery",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
"docs/cmdlets/"
],
"description": "Only 81 of ~169 exported cmdlets have markdown help docs in docs/cmdlets/. Newer cmdlets added since initial documentation pass are missing.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "PG-NEW3",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-5",
"verified_by": "scan",
"evidence": "170 markdown help docs in docs/cmdlets/ for 169 exported cmdlets. All cmdlets now have documentation."
}
},
{
"id": "F076",
"title": "No dependabot or renovate for dependency updates",
"category": "community",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-22",
"files": [
".github/"
],
"description": "No .github/dependabot.yml or renovate.json for automated dependency update PRs. NuGet and GitHub Actions versions will drift without manual monitoring.",
"scan_history": [
{
"scan_date": "2026-03-22",
"local_id": "CM-NEW1",
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "open"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": ".github/dependabot.yml created with nuget (weekly) and github-actions (weekly) ecosystems targeting main branch."
}
},
{
"id": "F077",
"title": "GetPveTaskListCmdlet VmId parameter missing ValidateRange",
"category": "code_quality",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
"src/PSProxmoxVE/Cmdlets/Tasks/GetPveTaskListCmdlet.cs"
],
"description": "Optional VmId parameter (int?, line 29) lacks [ValidateRange(100, 999999999)] attribute. Per D010, all VmId parameters must have ValidateRange regardless of whether mandatory or optional.",
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"decisions_ref": "D010",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "GetPveTaskListCmdlet.cs:29 now has [ValidateRange(100, 999999999)] on the VmId parameter, matching D010 convention."
}
},
{
"id": "F078",
"title": "No service-layer or PveHttpClient unit tests",
"category": "testing",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
"tests/PSProxmoxVE.Core.Tests/"
],
"description": "Zero xUnit tests for any of the 14 service classes (VmService, ContainerService, StorageService, etc.) or PveHttpClient. Model deserialization tests exist but no tests for API call construction, error handling, or business logic in services.",
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "196 new service unit tests across 10 test files in tests/PSProxmoxVE.Core.Tests/Services/. All 14 services now have IPveHttpClient constructor injection enabling Moq-based testing. Total test count: 382 (was 186)."
}
},
{
"id": "F079",
"title": "unit-tests.yml uses dotnet SDK 9.0.x, inconsistent with build.yml 10.0.x",
"category": "code_quality",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
".github/workflows/unit-tests.yml"
],
"description": "unit-tests.yml:31 installs dotnet-version 9.0.x while build.yml uses 10.0.x. The unit-tests workflow only builds netstandard2.0 and net48 so this works functionally, but the SDK versions should be consistent across workflows.",
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"decisions_ref": "D009",
"resolution": {
"scan_date": "2026-03-23",
"report_id": "remediation-1",
"verified_by": "code_change",
"evidence": "unit-tests.yml line 31 changed from dotnet-version 9.0.x to 10.0.x, matching build.yml and the test project net10.0 TFM."
}
},
{
"id": "F080",
"title": "Restart-PveContainer CI failure: ConfirmImpact.High blocks non-interactive CI",
"category": "ci_integration",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
"src/PSProxmoxVE/Cmdlets/Containers/RestartPveContainerCmdlet.cs",
"tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1"
],
"description": "Restart-PveContainer now has ConfirmImpact.High (F062 fix) which prompts for confirmation in non-interactive CI. The integration test at line 983 does not pass -Confirm:$false, causing a NullReferenceException when the cmdlet tries to read the confirmation response.",
"ci_context": {
"workflow": "integration-tests.yml",
"run_id": "23454616043",
"run_url": "https://github.com/goodolclint/PSProxmoxVE/actions/runs/23454616043",
"job_name": "integration (9)",
"platform": "ubuntu-latest/pwsh",
"pve_version": "pve9",
"conclusion": "failure",
"run_date": "2026-03-23T18:51:46Z"
},
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-6-fix",
"verified_by": "code_change",
"evidence": "Added -Confirm:$false to Restart-PveContainer call at Integration.Tests.ps1:983. All other destructive cmdlet calls in the integration test already had -Confirm:$false."
}
},
{
"id": "F081",
"title": "Copy-PveContainer CI failure: cascading from Restart-PveContainer failure",
"category": "ci_integration",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
"tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1"
],
"description": "Copy-PveContainer test fails with PveApiException: Cannot do full clones on a running container without snapshots. Container was still running because Restart-PveContainer (F080) failed to restart it. Test ordering dependency.",
"ci_context": {
"workflow": "integration-tests.yml",
"run_id": "23454616043",
"run_url": "https://github.com/goodolclint/PSProxmoxVE/actions/runs/23454616043",
"job_name": "integration (9)",
"platform": "ubuntu-latest/pwsh",
"pve_version": "pve9",
"conclusion": "failure",
"run_date": "2026-03-23T18:51:46Z"
},
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-6-fix",
"verified_by": "code_change",
"evidence": "Cascading failure from F080. With Restart-PveContainer now passing -Confirm:$false, the container will be properly stopped before the clone test runs."
}
},
{
"id": "F082",
"title": "PVE 8 integration: Docker container image not found",
"category": "ci_integration",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
".github/workflows/integration-tests.yml"
],
"description": "PVE 8 integration job fails at Initialize containers step — Docker image ghcr.io/goodolclint/psproxmoxve-integration:<sha> not found. The container-image build job may have failed or the image was not pushed for this commit.",
"ci_context": {
"workflow": "integration-tests.yml",
"run_id": "23454616043",
"run_url": "https://github.com/goodolclint/PSProxmoxVE/actions/runs/23454616043",
"job_name": "integration (8)",
"platform": "self-hosted/docker",
"pve_version": "pve8",
"conclusion": "failure",
"run_date": "2026-03-23T18:51:46Z"
},
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-7",
"verified_by": "scan",
"evidence": "Integration test run 23461565300 (SHA 1faaba0, 2026-03-23T21:42:02Z) succeeded on all jobs including container-image build and both PVE 8 and PVE 9 test matrices. Docker image is now built and found correctly."
}
},
{
"id": "F083",
"title": "CONTRIBUTING.md references .NET SDK 9.0+ (should be 10.0+)",
"category": "community",
"severity": "low",
"status": "resolved",
"first_detected": "2026-03-23",
"files": [
"CONTRIBUTING.md"
],
"description": "CONTRIBUTING.md line 9 says \".NET SDK 9.0+\" but all workflows use dotnet SDK 10.0.x and the test project targets net10.0. Contributors following CONTRIBUTING.md will install the wrong SDK version.",
"scan_history": [
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-03-23",
"local_id": null,
"status": "resolved"
}
],
"resolution": {
"scan_date": "2026-03-23",
"report_id": "scan-6-fix",
"verified_by": "code_change",
"evidence": "CONTRIBUTING.md line 9 changed from .NET SDK 9.0+ to .NET SDK 10.0+, matching all CI workflows and test project TFM."
}
},
{
"id": "F084",
"title": "PveSession exposes auth secrets in default pipeline output",
"category": "security",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-03-24",
"files": [
"src/PSProxmoxVE.Core/Authentication/PveSession.cs",
"src/PSProxmoxVE/PSProxmoxVE.format.ps1xml"
],
"description": "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 default table/list view. A user piping $session | Format-List * or logging verbose output could inadvertently expose auth tokens. Add a PSProxmoxVE.format.ps1xml entry for PveSession that shows only Hostname, Port, AuthMode, IsExpired, and ServerVersion by default.",
"scan_history": [
{
"scan_date": "2026-03-24",
"local_id": null,
"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",
"title": "PveClusterJoinInfo exposes Newtonsoft JArray/JObject properties",
"category": "type_safety",
"severity": "high",
"status": "resolved",
"first_detected": "2026-03-25",
"decisions_ref": "D013",
"files": [
"src/PSProxmoxVE.Core/Models/Cluster/PveClusterJoinInfo.cs",
"src/PSProxmoxVE/Cmdlets/Cluster/GetPveClusterJoinInfoCmdlet.cs"
],
"description": "PveClusterJoinInfo.Nodelist is typed as JArray? and PveClusterJoinInfo.Totem is typed as JObject?. These Newtonsoft types are emitted directly to the PowerShell pipeline via Get-PveClusterJoinInfo, causing broken Get-Member, Format-Table, and Select-Object behavior. Nodelist should be List<Dictionary<string, object?>>? and Totem should be Dictionary<string, object?>?, with appropriate [JsonConverter] attributes for deserialization.",
"scan_history": [
{
"scan_date": "2026-03-25",
"local_id": "D013-scan-1",
"status": "new"
},
{
"scan_date": "2026-03-25",
"local_id": "D013-scan-2",
"status": "confirmed_open"
},
{
"scan_date": "2026-03-25",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-03-25",
"evidence": "Replaced JArray? Nodelist with List<Dictionary<string, object?>>? + NativeListConverter, and JObject? Totem with Dictionary<string, object?>? + NativeDictionaryConverter. Removed Newtonsoft.Json.Linq dependency.",
"verified_by": "dotnet build + dotnet test (382 passed)"
}
},
{
"id": "F086",
"title": "New-PveVm -DiskSize and New-PveContainer -RootFsSize pass unit suffix verbatim, LVM rejects 'NG'",
"category": "api_contract",
"severity": "high",
"status": "resolved",
"first_detected": "2026-05-20",
"github_issue": 58,
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Containers/NewPveContainerCmdlet.cs"
],
"description": "Disk and rootfs sizes are interpolated directly into the disk spec as '<storage>:<size>'. The parameter docstring advertises 'e.g. 32G' but on LVM/LVM-thin storages PVE parses the value after the colon as a volume name unless it is a bare integer, failing with 'unable to parse lvm volume name \"32G\"'. File-backed storages (NFS, directory) accept either form, masking the bug in mixed environments.",
"scan_history": [
{
"scan_date": "2026-05-20",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-05-20",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-05-20",
"evidence": "Added SizeParser.NormalizeToGibibytes() which strips G/GB/T/TB suffixes, rejects sub-GB units with a clear error, and converts TB overflows to ArgumentException. New-PveVm and New-PveContainer normalize -DiskSize and -RootFsSize before ShouldProcess so typos are caught with -WhatIf, regardless of whether the matching -DiskStorage/-RootFsStorage was supplied.",
"verified_by": "dotnet build + dotnet test (577 passed, 32 SizeParserTests) + Pester (39 passed, new DiskSize/RootFsSize validation contexts)"
}
},
{
"id": "F087",
"title": "HttpClient uses 100s default timeout; Send-PveFile and other long-running calls fail on large payloads",
"category": "reliability",
"severity": "high",
"status": "resolved",
"first_detected": "2026-05-20",
"github_issue": 59,
"files": [
"src/PSProxmoxVE.Core/Client/PveHttpClient.cs",
"src/PSProxmoxVE.Core/Authentication/PveSession.cs",
"src/PSProxmoxVE.Core/Authentication/PveAuthenticator.cs",
"src/PSProxmoxVE/Cmdlets/Storage/SendPveFileCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Storage/InvokePveStorageDownloadCmdlet.cs",
"src/PSProxmoxVE/Cmdlets/Connection/ConnectPveServerCmdlet.cs"
],
"description": "PveHttpClient constructs HttpClient without setting Timeout, so .NET's 100s default applies to every request. Send-PveFile, Invoke-PveStorageDownload, and Connect-PveServer expose no way to override it, so multi-GB ISO uploads on a real LAN reliably trip the 100s timeout with TaskCanceledException. PveHttpClient.SendAsync also failed to surface the timeout as a PveApiException, leaking the raw TaskCanceledException to callers.",
"scan_history": [
{
"scan_date": "2026-05-20",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-05-20",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-05-20",
"evidence": "Added PveSession.Timeout (default 100s) and a TimeSpan? override on PveHttpClient. Connect-PveServer exposes -TimeoutSeconds to set the session-default; Send-PveFile and Invoke-PveStorageDownload expose -TimeoutSeconds with a 30-minute implicit default so large uploads/downloads do not trip the 100s HttpClient default. -TimeoutSeconds 0 means Timeout.InfiniteTimeSpan. PveHttpClient.SendAsync now catches the TimeoutException-wrapped TaskCanceledException and rethrows it as PveApiException(RequestTimeout) with the resource path.",
"verified_by": "dotnet build + dotnet test (passed including new SendAsync_TimeoutFires xUnit test) + Pester (-TimeoutSeconds coverage on all three cmdlets)"
}
},
{
"id": "F088",
"title": "Form values containing ';' are split into bogus fields (boot order breaks Set-PveVmConfig)",
"category": "api_contract",
"severity": "high",
"status": "resolved",
"first_detected": "2026-05-22",
"github_issue": 64,
"files": [
"src/PSProxmoxVE.Core/Client/PveHttpClient.cs"
],
"description": "PveHttpClient.EncodeFormValue percent-encoded &, =, +, space, and % but not ';'. PVE's application/x-www-form-urlencoded parser treats a raw ';' as a field separator, so a value like boot=order=scsi0;ide2 was split into 'boot=order=scsi0' plus an empty 'ide2' field, which PVE rejected with 'ide2: unable to parse drive options'. Affected any config value containing ';' (boot order, hookscript, some args).",
"scan_history": [
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-05-22",
"evidence": "Added ';' -> %3B to EncodeFormValue. Safe under the minimal-encoding policy (cluster-join values never contain ';' and PVE url-decodes config form values). Verified the colon/comma minimal-encoding behavior is preserved.",
"verified_by": "dotnet build + dotnet test (3 new PveHttpClientFormEncodingTests: semicolon encoded on POST/PUT, comma/colon remain literal)"
}
},
{
"id": "F089",
"title": "New-PveVm cannot set disk controller or IO options at create time",
"category": "enhancement",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-05-22",
"github_issue": 65,
"files": [
"src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs"
],
"description": "New-PveVm only emitted a plain virtio0 from -DiskStorage/-DiskSize/-DiskFormat. There was no way to choose the controller (virtio-scsi-single) or set performance/IO options (iothread, aio, ssd, discard, cache) at create time, forcing callers to create the VM diskless and then hand-build a raw scsi0 string via Set-PveVmConfig.",
"scan_history": [
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-05-22",
"evidence": "Added -DiskBus (virtio/scsi/sata/ide, default virtio), -ScsiHardware (scsihw), -DiskIoThread, -DiskAio, -DiskSsd, -DiskDiscard, -DiskCache. The disk spec is built via a BuildDiskSpec helper; ValidateDiskOptions enforces (before ShouldProcess) that ssd is not used on virtio and that iothread is only used on virtio or scsi+virtio-scsi-single, surfacing clear errors at create time instead of at VM start.",
"verified_by": "dotnet build (0 warnings) + Pester (47 New-PveVm tests incl. disk-option validation matrix)"
}
},
{
"id": "F090",
"title": "Get-PveVmConfig silently drops config keys not in the typed allow-list",
"category": "completeness",
"severity": "medium",
"status": "resolved",
"first_detected": "2026-05-22",
"github_issue": 65,
"files": [
"src/PSProxmoxVE.Core/Models/Vms/PveVmConfig.cs"
],
"description": "PveVmConfig was a fixed allow-list of [JsonProperty] fields with no catch-all, so keys like scsihw, efidisk0, tpmstate0, hostpci0, and additional disk buses were silently dropped on deserialize — making the F089 workaround unverifiable by reading the config back.",
"scan_history": [
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-05-22",
"evidence": "Added typed scsihw/efidisk0/tpmstate0 properties plus a private [JsonExtensionData] landing field exposed as AdditionalProperties (Dictionary<string, object?> via JsonHelper.ToNative, so values are native .NET types per D013 — no JToken leakage). Typed keys do not duplicate into the catch-all.",
"verified_by": "dotnet build + dotnet test (586 passed, new VmModelTests for scsihw/efidisk/tpm, native-typed AdditionalProperties, and no typed-key leakage)"
}
},
{
"id": "F091",
"title": "Invoke-PveVmGuestExec -Args delivered as JSON STDIN instead of argv",
"category": "api_contract",
"severity": "high",
"status": "resolved",
"first_detected": "2026-05-22",
"github_issue": 68,
"files": [
"src/PSProxmoxVE.Core/Services/VmService.cs",
"src/PSProxmoxVE.Core/Client/PveHttpClient.cs",
"src/PSProxmoxVE.Core/Client/IPveHttpClient.cs"
],
"description": "VmService.ExecuteGuestCommand JSON-serialized the args array into the agent/exec 'input-data' field (the process's STDIN) instead of passing them as argv. PVE's agent/exec 'command' parameter is itself an array (element 0 = executable, rest = arguments) and must be sent as repeated form keys. The result: guest commands ran with no/garbage arguments (cmd.exe started interactively, the JSON blob appeared at the prompt). The low-level client also could not express repeated form keys (Dictionary<string,string> only).",
"scan_history": [
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "new"
},
{
"scan_date": "2026-05-22",
"local_id": null,
"status": "fixed"
}
],
"resolution": {
"scan_date": "2026-05-22",
"evidence": "Added PostAsync(string, IEnumerable<KeyValuePair<string,string>>) to IPveHttpClient/PveHttpClient (BuildFormContent now emits repeated keys). ExecuteGuestCommand builds command = [exe] + args as repeated 'command' fields and no longer touches input-data.",
"verified_by": "dotnet build (0 warnings) + dotnet test (594 passed; new PveHttpClientFormEncodingTests for repeated keys + per-value encoding, and VmServiceTests asserting the command array and absence of input-data)"
}
}
]
}