Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **Enabled `EnforceCodeStyleInBuild`** (NextIteration.Standards §1.2.1, now a `MUST`). The
canonical `.editorconfig`'s gated rules now fail the build instead of merely showing in
the IDE, so the house style is enforced. Bringing the code green under the flag was a
mechanical, behavior-preserving reformat — braces on all single-statement `if`s, block-
scoped namespaces (the interop layer and tests were file-scoped), and minor
expression-body/accessibility/collection-expression fixes — applied with `dotnet format`.
`IDE0005` is suppressed in the **test** project only: it requires `GenerateDocumentationFile`,
which §2.7 sets to `false` for tests, so gating it there would hard-error; it still gates
the shipping project. (§2.7 needs the matching amendment estate-wide.)

- **Adopted the revised canonical `.editorconfig`** (NextIteration.Standards §5.2). The new
file is a deliberate allow-list of gated style rules (no blanket
`dotnet_analyzer_diagnostic.severity`) and fixes the private-field naming rule that had
Expand Down
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<AnalysisLevel>latest</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnablePackageValidation>true</EnablePackageValidation>
<IncludeSymbols>true</IncludeSymbols>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NextIteration.SpectreConsole.Auth.Commands;

using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Spectre.Console.Cli;
using System.ComponentModel;

using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Spectre.Console;
using System.ComponentModel;

using NextIteration.SpectreConsole.Auth.Persistence;

using Spectre.Console;
using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ internal static class CommandFormatting
/// </summary>
internal static string ShortId(string? accountId)
{
if (string.IsNullOrEmpty(accountId)) return string.Empty;
if (string.IsNullOrEmpty(accountId))
{
return string.Empty;
}

return accountId.Length >= 8 ? accountId[..8] + "..." : accountId;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Spectre.Console;
using System.ComponentModel;

using NextIteration.SpectreConsole.Auth.Persistence;

using Spectre.Console;
using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Spectre.Console;
using System.ComponentModel;

using NextIteration.SpectreConsole.Auth.Persistence;
using NextIteration.SpectreConsole.Auth.Portability;

using Spectre.Console;
using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Spectre.Console;
using System.ComponentModel;

using NextIteration.SpectreConsole.Auth.Persistence;
using NextIteration.SpectreConsole.Auth.Portability;

using Spectre.Console;
using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Spectre.Console;
using System.ComponentModel;

using NextIteration.SpectreConsole.Auth.Persistence;

using Spectre.Console;
using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Spectre.Console;
using System.ComponentModel;

using NextIteration.SpectreConsole.Auth.Persistence;

using Spectre.Console;
using Spectre.Console.Cli;

namespace NextIteration.SpectreConsole.Auth.Commands
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ public interface ICredential
/// Must be unique across providers and stable across versions — it
/// is embedded in the filename of each stored credential.
/// </summary>
public abstract static string ProviderName { get; }
abstract static string ProviderName { get; }

/// <summary>
/// List of environment names the provider accepts (for example
/// <c>Production</c>, <c>Staging</c>). Used to populate the
/// environment-selection prompt during <c>accounts add</c>.
/// </summary>
public abstract static List<string> SupportedEnvironments { get; }
abstract static List<string> SupportedEnvironments { get; }

/// <summary>
/// The environment this particular credential instance targets.
/// Must be one of the values returned by <see cref="SupportedEnvironments"/>.
/// </summary>
public abstract string Environment { get; }
abstract string Environment { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ public static class CredentialEncryptionFactory
/// <see cref="LocalFileCredentialEncryption"/> — see its remarks for
/// the security implications of supplying it.
/// </param>
public static ICredentialEncryption Create(string credentialsDirectory, byte[]? additionalEntropy = null)
{
return new LocalFileCredentialEncryption(credentialsDirectory, additionalEntropy);
}
public static ICredentialEncryption Create(string credentialsDirectory, byte[]? additionalEntropy = null) => new LocalFileCredentialEncryption(credentialsDirectory, additionalEntropy);

/// <summary>
/// Creates the file-based, cross-platform encryption implementation explicitly.
Expand All @@ -33,10 +30,7 @@ public static ICredentialEncryption Create(string credentialsDirectory, byte[]?
/// <see cref="LocalFileCredentialEncryption"/> — see its remarks for
/// the security implications of supplying it.
/// </param>
public static ICredentialEncryption CreateLocalFile(string credentialsDirectory, byte[]? additionalEntropy = null)
{
return new LocalFileCredentialEncryption(credentialsDirectory, additionalEntropy);
}
public static ICredentialEncryption CreateLocalFile(string credentialsDirectory, byte[]? additionalEntropy = null) => new LocalFileCredentialEncryption(credentialsDirectory, additionalEntropy);

/// <summary>
/// Creates a Windows DPAPI encryption implementation. Windows only —
Expand All @@ -46,9 +40,6 @@ public static ICredentialEncryption CreateLocalFile(string credentialsDirectory,
/// </summary>
/// <exception cref="PlatformNotSupportedException">Not running on Windows.</exception>
[SupportedOSPlatform("windows")]
public static ICredentialEncryption CreateDpapi()
{
return new DpapiCredentialEncryption();
}
public static ICredentialEncryption CreateDpapi() => new DpapiCredentialEncryption();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public DpapiCredentialEncryption()
public Task<string> EncryptAsync(string plainText)
{
if (string.IsNullOrEmpty(plainText))
{
return Task.FromResult(string.Empty);
}

try
{
Expand All @@ -50,7 +52,9 @@ public Task<string> EncryptAsync(string plainText)
public Task<string> DecryptAsync(string encryptedText)
{
if (string.IsNullOrEmpty(encryptedText))
{
return Task.FromResult(string.Empty);
}

try
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using NextIteration.SpectreConsole.Auth.Persistence;
using System.Security.Cryptography;
using System.Text;

using NextIteration.SpectreConsole.Auth.Persistence;

namespace NextIteration.SpectreConsole.Auth.Encryption
{
/// <summary>
Expand Down Expand Up @@ -134,7 +135,9 @@ public LocalFileCredentialEncryption(string credentialsDirectory, byte[]? additi
public async Task<string> EncryptAsync(string plainText)
{
if (string.IsNullOrEmpty(plainText))
{
return string.Empty;
}

try
{
Expand Down Expand Up @@ -168,7 +171,9 @@ public async Task<string> EncryptAsync(string plainText)
public async Task<string> DecryptAsync(string encryptedText)
{
if (string.IsNullOrEmpty(encryptedText))
{
return string.Empty;
}

byte[] input;
try
Expand Down Expand Up @@ -338,7 +343,9 @@ private static byte[] EncryptWithGcm(byte[] key, byte[] plaintext)
private static byte[] DecryptWithGcm(byte[] key, byte[] input)
{
if (input.Length < NonceSize + TagSize)
{
throw new InvalidOperationException("Encrypted payload is shorter than the AES-GCM header.");
}

var nonce = new byte[NonceSize];
var tag = new byte[TagSize];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,16 @@ private static async Task ReplaceAtomicallyAsync(string tempPath, string path)
}
}

private static string BuildTempPath(string finalPath) =>
// Unique per call to avoid collisions between concurrent writers,
// who would otherwise both want the same `{path}.tmp` name.
$"{finalPath}.{Guid.NewGuid():N}.tmp";
// Unique per call to avoid collisions between concurrent writers, who
// would otherwise both want the same `{path}.tmp` name.
private static string BuildTempPath(string finalPath) => $"{finalPath}.{Guid.NewGuid():N}.tmp";

private static void ApplyUnixModeIfRequested(string path, UnixFileMode? unixMode)
{
if (unixMode is null || OperatingSystem.IsWindows())
{
return;
}

File.SetUnixFileMode(path, unixMode.Value);
}
Expand All @@ -123,7 +124,9 @@ private static void TryDelete(string path)
try
{
if (File.Exists(path))
{
File.Delete(path);
}
}
catch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ internal static class CredentialsDirectory
internal static void Ensure(string path)
{
if (Directory.Exists(path))
{
return;
}

if (OperatingSystem.IsWindows())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ await AtomicFile.WriteAllTextAsync(
/// <inheritdoc />
public async Task<bool> DeleteCredentialAsync(string accountId)
{
if (!IsValidAccountId(accountId)) return false;
if (!IsValidAccountId(accountId))
{
return false;
}

var found = await FindCredentialByAccountIdAsync(accountId).ConfigureAwait(false);
if (found is null)
Expand Down Expand Up @@ -174,7 +177,10 @@ public async Task<bool> DeleteCredentialAsync(string accountId)
/// <inheritdoc />
public async Task<bool> SelectCredentialAsync(string accountId)
{
if (!IsValidAccountId(accountId)) return false;
if (!IsValidAccountId(accountId))
{
return false;
}

var found = await FindCredentialByAccountIdAsync(accountId).ConfigureAwait(false);
if (found is null)
Expand Down
Loading