mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-09-04 03:05:32 +00:00
b90791e2bf
D001-D021 become ADR 0001-0021 in docs/decisions/, one decision per file. D017's PESTER_VERSION amendment was a second decision in one entry and becomes ADR 0022. ADR 0023 records the migration and reverses the lane2-change-plan ruling that deliberately kept DECISIONS.md until the CI lane work landed. DECISIONS.md is reduced to a stub with a D-to-ADR redirect table, so the four released CHANGELOG entries and older issue bodies that cite it degrade to a redirect rather than a dead reference. docs/review/ and docs/lane2-change-plan.md are deleted (ADR 0024). Of 91 findings, 83 were resolved and six of the seven still open were already GitHub issues; F021 was the exception and is now #130. CLAUDE.md's Key Conventions list gains the two rules it was missing and becomes the checklist, with the ADRs carrying rationale.
49 lines
1.7 KiB
Markdown
49 lines
1.7 KiB
Markdown
# ADR 0002 — Password parameters must use SecureString
|
|
|
|
- **Status:** Accepted
|
|
- **Date:** 2026-03-22
|
|
- **Deciders:** unrecorded; adopted during review scan 2026-03-22
|
|
- **Context source:** `docs/review/findings.json` F051
|
|
|
|
## Context
|
|
|
|
`Set-PveVmGuestPassword` accepted a plain `string` password parameter, leaving the credential in managed memory indefinitely.
|
|
|
|
`Connect-PveServer` already took a `PSCredential`, so the module was inconsistent with itself about how sensitive input arrives.
|
|
|
|
## Decision
|
|
|
|
Every cmdlet parameter that accepts a password is a `SecureString`, extracted with `Marshal.SecureStringToGlobalAllocUnicode` and freed with `ZeroFreeGlobalAllocUnicode` in a `finally`.
|
|
|
|
```csharp
|
|
[Parameter(Mandatory = true)]
|
|
public SecureString Password { get; set; }
|
|
|
|
IntPtr ptr = IntPtr.Zero;
|
|
try
|
|
{
|
|
ptr = Marshal.SecureStringToGlobalAllocUnicode(Password);
|
|
string plainText = Marshal.PtrToStringUni(ptr);
|
|
}
|
|
finally
|
|
{
|
|
if (ptr != IntPtr.Zero)
|
|
Marshal.ZeroFreeGlobalAllocUnicode(ptr);
|
|
}
|
|
```
|
|
|
|
## Rejected alternatives
|
|
|
|
A plain `string` parameter. It is simpler to write and to test, and it leaves the credential recoverable from a memory dump for the lifetime of the process:
|
|
|
|
```csharp
|
|
[Parameter(Mandatory = true)]
|
|
public string Password { get; set; }
|
|
```
|
|
|
|
## Consequences
|
|
|
|
The service layer below the cmdlet still receives a plain `string` — the conversion happens at the cmdlet boundary, and `ClusterConfigService.JoinCluster` documents that it expects the converted value. The guarantee is about the module's public surface and the window of exposure, not about the credential never existing in managed memory.
|
|
|
|
A TLS private key is at least as sensitive as a password; anything accepting one is covered by the same rule.
|