mirror of https://github.com/raandree/NTFSSecurity
Browse Source
Add-NTFSAccess, Remove-NTFSAccess, Add-NTFSAudit, and Remove-NTFSAudit silently kept the default value of AccessType, AuditFlags, InheritanceFlags, PropagationFlags, and AppliesTo when a piped object's property could not be converted to the parameter's type: a misspelled AccessType such as "Denyy", or a blank CSV cell, created an Allow entry with no message. For a permissions tool, a typo for Deny that silently grants access is a security-relevant defect (Decision 27). The five parameters are now declared as object and converted inside the cmdlet, the way the PowerShell binder converts a typed parameter (names in any case, comma-separated flag lists, numbers, enum instances). A value that cannot be converted, including a blank one, is now an error named InvalidParameterValue: terminating and catchable when it is named on the command line, exactly as the binder reported it before; a non-terminating error targeting that path or descriptor when it comes from a piped object, which leaves that item unchanged and lets the pipeline continue with the next one. A $null value is still not an error and keeps the parameter's default. Tab completion of the five parameters keeps working through explicit argument completers. Choosing between the Simple and Complex parameter sets is unaffected and still rejects AppliesTo together with InheritanceFlags or PropagationFlags as AmbiguousParameterSet, for every mix of string and enum values. The four cmdlet pages document the parameters as Type: Object, list the valid values, and describe the new error; a sentence that said a piped AppliesTo typo or blank cell was silently ignored is corrected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant <ai@example.com>pull/131/head
13 changed files with 1341 additions and 124 deletions
@ -0,0 +1,87 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Management.Automation; |
|||
using System.Management.Automation.Language; |
|||
using System.Security.AccessControl; |
|||
using Security2; |
|||
|
|||
namespace NTFSSecurity |
|||
{ |
|||
// Completes the names of an enumeration for a parameter that is declared as object (Decision 27, fix B) and so
|
|||
// does not get the completion that a parameter of an enumeration type gets from PowerShell itself.
|
|||
internal static class EnumArgumentCompletion |
|||
{ |
|||
/// <summary>
|
|||
/// Returns the names of the enumeration that start with the word the user already typed, compared without
|
|||
/// regard to case, in the order Enum.GetNames returns them.
|
|||
/// </summary>
|
|||
/// <param name="enumType">The enumeration to complete the names of.</param>
|
|||
/// <param name="wordToComplete">The word the user already typed; empty or null completes every name.</param>
|
|||
internal static IEnumerable<CompletionResult> Complete(Type enumType, string wordToComplete) |
|||
{ |
|||
var word = wordToComplete ?? string.Empty; |
|||
var results = new List<CompletionResult>(); |
|||
|
|||
foreach (var name in Enum.GetNames(enumType)) |
|||
{ |
|||
if (name.StartsWith(word, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
results.Add(new CompletionResult(name, name, CompletionResultType.ParameterValue, name)); |
|||
} |
|||
} |
|||
|
|||
return results; |
|||
} |
|||
} |
|||
|
|||
/// <summary>Completes the names of System.Security.AccessControl.AccessControlType for -AccessType.</summary>
|
|||
public class AccessControlTypeArgumentCompleter : IArgumentCompleter |
|||
{ |
|||
public IEnumerable<CompletionResult> CompleteArgument( |
|||
string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) |
|||
{ |
|||
return EnumArgumentCompletion.Complete(typeof(AccessControlType), wordToComplete); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Completes the names of System.Security.AccessControl.AuditFlags for -AuditFlags.</summary>
|
|||
public class AuditFlagsArgumentCompleter : IArgumentCompleter |
|||
{ |
|||
public IEnumerable<CompletionResult> CompleteArgument( |
|||
string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) |
|||
{ |
|||
return EnumArgumentCompletion.Complete(typeof(AuditFlags), wordToComplete); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Completes the names of System.Security.AccessControl.InheritanceFlags for -InheritanceFlags.</summary>
|
|||
public class InheritanceFlagsArgumentCompleter : IArgumentCompleter |
|||
{ |
|||
public IEnumerable<CompletionResult> CompleteArgument( |
|||
string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) |
|||
{ |
|||
return EnumArgumentCompletion.Complete(typeof(InheritanceFlags), wordToComplete); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Completes the names of System.Security.AccessControl.PropagationFlags for -PropagationFlags.</summary>
|
|||
public class PropagationFlagsArgumentCompleter : IArgumentCompleter |
|||
{ |
|||
public IEnumerable<CompletionResult> CompleteArgument( |
|||
string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) |
|||
{ |
|||
return EnumArgumentCompletion.Complete(typeof(PropagationFlags), wordToComplete); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Completes the names of Security2.ApplyTo for -AppliesTo.</summary>
|
|||
public class ApplyToArgumentCompleter : IArgumentCompleter |
|||
{ |
|||
public IEnumerable<CompletionResult> CompleteArgument( |
|||
string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) |
|||
{ |
|||
return EnumArgumentCompletion.Complete(typeof(ApplyTo), wordToComplete); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using System; |
|||
using System.Management.Automation; |
|||
|
|||
namespace NTFSSecurity |
|||
{ |
|||
/// <summary>
|
|||
/// Converts the raw value of a parameter that is declared as object back into the enumeration the parameter
|
|||
/// describes, the way the PowerShell binder converts an argument for a typed parameter. AccessType, AuditFlags,
|
|||
/// InheritanceFlags, PropagationFlags, and AppliesTo of the access and audit cmdlets are declared as object
|
|||
/// (Decision 27, fix B), so that the binder does not silently skip a piped property it cannot convert and keep
|
|||
/// the parameter at its default; this class gives the cmdlet the same conversion, with a message it can report
|
|||
/// as its own error instead of a binding error. This class writes nothing; the cmdlet writes the error.
|
|||
/// </summary>
|
|||
internal static class ParameterConversion |
|||
{ |
|||
/// <summary>
|
|||
/// Converts the raw value of a parameter to T, the way the PowerShell binder converts an argument for a
|
|||
/// parameter of that type: names in any case, a comma-separated list for a flags enumeration, a number, or
|
|||
/// an enumeration instance.
|
|||
/// </summary>
|
|||
/// <param name="value">
|
|||
/// The raw value of the parameter. Not null: the caller keeps the default of the parameter for null instead
|
|||
/// of calling this method, because null means that the parameter was not specified.
|
|||
/// </param>
|
|||
/// <param name="parameterName">The name of the parameter, without the leading dash, for the message.</param>
|
|||
/// <returns>The converted value.</returns>
|
|||
/// <exception cref="ArgumentException">
|
|||
/// The value is blank (an empty or a white-space-only string, the usual blank CSV cell) or the conversion
|
|||
/// failed. The message names the value, unless it is blank, the parameter, and the valid names of T; the
|
|||
/// inner exception of a failed conversion is the error that LanguagePrimitives.ConvertTo raised.
|
|||
/// </exception>
|
|||
internal static T Convert<T>(object value, string parameterName) |
|||
{ |
|||
var text = value as string; |
|||
if (text != null && string.IsNullOrWhiteSpace(text)) |
|||
{ |
|||
throw new ArgumentException(string.Format( |
|||
"The value for parameter -{0} is blank. {1}", parameterName, DescribeValidValues(typeof(T)))); |
|||
} |
|||
|
|||
try |
|||
{ |
|||
return (T)LanguagePrimitives.ConvertTo(value, typeof(T)); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw new ArgumentException( |
|||
string.Format("The value '{0}' for parameter -{1} is not valid. {2}", value, parameterName, DescribeValidValues(typeof(T))), |
|||
ex); |
|||
} |
|||
} |
|||
|
|||
// The valid names of the enumeration, in declaration order, with a note for a flags enumeration that a
|
|||
// combination of them is written separated with commas.
|
|||
private static string DescribeValidValues(Type enumType) |
|||
{ |
|||
var names = string.Join(", ", Enum.GetNames(enumType)); |
|||
|
|||
if (enumType.IsDefined(typeof(FlagsAttribute), false)) |
|||
{ |
|||
return string.Format("Valid values: {0}. Combine several with a comma.", names); |
|||
} |
|||
|
|||
return string.Format("Valid values: {0}.", names); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue