pspete / PoShPACLI

Powershell Module for CyberArk PACLI
MIT License
72 stars 19 forks source link

Issue parsing output in the Find-PVFile function when account contains attributes with multi-line text #72

Open InconstantRO opened 3 years ago

InconstantRO commented 3 years ago

Your issue may already be reported. Please search existing issues before creating one.

Your Environment

Find-PVFile function should provide the list of files and deatails about each file in the safe.

Current Behaviour

This function stops returning correct file details after one of the files contains attribute with multi-line content. For example, if FileCategories attribute contains CPMErrorDetails value wich spans on multiple lines.

Possible Solution

Before sending output to ConvertFrom-PacliOutput normalize it to avoid new lines and also # characters in the CPM error. Here is example code:

                # Set the regex to capture multi-line CPM error message located between "#CPMErrorDetails:" and "#ResetImmediately" or "#CPMDisabled:" (note: could be also other, if reset or cmp disabled parameters are not present).
                [regex]$captureErrorRegex = "(.*\#CPMErrorDetails:)([\S\s]*?)(\#ResetImmediately:.*|\#CPMDisabled:.*)"
                # Remove empty lines from the output
                $outputNoNewLine = $Return.StdOut | Select-String -Pattern "\S"
                # Split by new lines
                $splitOutput = $outputNoNewLine -split "\r\n"
                # Init new output statements list
                $newOutputStatements = @()
                # Loop through each statement in the split output
                for ([int]$i=0; $i -lt $splitOutput.Count; $i++) {
                    # Get statement
                    $originalStatement = $splitOutput[$i]
                    # Match regex against statement
                    $match = $captureErrorRegex.Matches($originalStatement)
                    # Check if match succeeded
                    if ($match.Success) {
                        Write-Host "Found CPM error. Normalize it."
                        # Get error value, which matches regex
                        $cpmErrorValue = $match.Groups[2].Value
                        # Replace possible new lines with comma
                        $cpmErrorValue = $cpmErrorValue -replace "\r\n",", " -replace "\r",", " -replace "\n",", "
                        # Replace double qoutes with single quotes
                        $cpmErrorValue = $cpmErrorValue -replace '"',''''
                        # Replace # character with % character (because it is used later to parse and split file categories)
                        $cpmErrorValue = $cpmErrorValue -replace '#','%'
                        Write-Host "New CPM error:"
                        Write-Host $cpmErrorValue
                        # Add new statement to the list by combining pre- and post- statement values
                        $newOutputStatements += $match.Groups[1].Value + $cpmErrorValue + $match.Groups[3].Value
                        # $newOutputStatements += $match.Groups[1].Value + "---------ERROR----------" + $match.Groups[3].Value
                    } else {
                        # No match
                        Write-Host "Couldn't find CPM error."
                        # Add orignal statement as is
                        $newOutputStatements += $originalStatement
                    }
                }
                # Create new output by joining statements with new lines
                $newOutput = $newOutputStatements -join "`r`n"

                #Convert Output to array
                #.. your code goes below

Also it could be good to check the $Results.length to make sure that it can be divided by 32 without leftovers. If you get floating number, by division on 32, then your properties are shifted.

Steps to Reproduce (for bug reports)

1 . Create some attribute on account which accepts multiline input and set it.

2 . Add several accounts in the safe. All of them may contain this multiline attribute set or only some accoutns may have this attribute set.

3 . Try to use Find-PVFile function to query all accounts in the safe.

Here is example output, before it being processed by ConvertFrom-PacliOutput function, you can use it to reproduce the issue. Here I have 2 accounts in the safe:

"MyPlatformName1-MyAddress1-CYBERARK_TESTING","","Thu May 27 12:41:20 2021","CPMv20","--","","Thu May 27 12:41:20 2021","CPMv20","--","","","500","NO","000000000000052","MySafeName1","Root","5","-1","PENDING","Fri May 07 09:08:48 2021","user.name","Mon May 17 15:54:51 2021","usr1","Mon May 17 15:54:51 2021","usr1","Thu May 27 12:41:20 2021","CPMv20","Thu May 27 12:41:20 2021","CPMv20","--","","UserName:CYBERARK_TESTING#PolicyID:MyTestPlatform#DeviceType:Imported Platforms#Address:MyAddress#CreationMethod:PVWA#ServiceName:MySvcName1#CPMErrorDetails:Execution error. EXT01::Did not find any matching prompt for 'Traceback (most recent call last):
  File "C:\Program Files (x86)\CyberArk\Password Manager\bin\MyScript.py", line 18, in <module>
    temp.click()
  File "C:\Program Files (x86)\Python39-32\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click
    self._execute(Command.CLICK_ELEMENT)
  File "C:\Program Files (x86)\Python39-32\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute
    return self._parent.execute(command, params)
  File "C:\Program Files (x86)\Python39-32\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "C:\Program Files (x86)\Python39-32\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.ElementNotVisibleException: Message: element not visible
  (Session info: chrome=90.0.4430.93)
  (Driver info: chromedriver=2.35.528161 (5b82f2d2aae0ca24b877009200ced9065a772e73),platform=Windows NT 6.3.9600 x86_64)

'. Error code:-1#ResetImmediately:ChangeTask#RetriesCount:1#CPMStatus:failure#LastTask:ChangeTask#LastFailDate:1622112081#CPMDisabled:(CPM)MaxRetries#"
"ztest1","","Wed Jun 23 14:51:03 2021","username2","--","","Wed Jun 23 14:51:03 2021","username2","--","","","500","NO","000000000000053","MySafeName1","Root","6","-1","PENDING","Wed Jun 23 14:51:03 2021","username2","Wed Jun 23 14:51:03 2021","username2","--","","--","","--","","--","","ServiceName:xtestsvc#DeviceType:Imported Platforms#UserName:xtestusr1#PolicyID:MyTestPlatform#Address:xtestaddr1#CreationMethod:PVWA#"

4 . Check the output and notice that after account with multi-line attribute all properties of other accounts would not be displayed correctly, because in the function below you expect that each property is located in a separate line, but in our case lines are shifted down because of multi-line property value:

                #loop through results
                For ($i = 0 ; $i -lt $Results.length ; $i += 32) {

                    #Get Range from array
                    $values = $Results[$i..($i + 32)]

                    #Output Object
                    [PSCustomObject] @{

                        "Filename"                   = $values[0]
                        "Accessed"                   = $values[1]
                        "CreationDate"               = $values[2]
                        "CreatedBy"                  = $values[3]
                        "DeletionDate"               = $values[4]
                        "DeletionBy"                 = $values[5]
                        "LastUsedDate"               = $values[6]
                        "LastUsedBy"                 = $values[7]
                        "LockDate"                   = $values[8]
                        "LockedBy"                   = $values[9]
                        "LockedByGW"                 = $values[10]
                        "Size"                       = $values[11]
                        "History"                    = $values[12]
                        "InternalName"               = $values[13]
                        "Safe"                       = $values[14]
                        "Folder"                     = $values[15]
                        "FileID"                     = $values[16]
                        "LockedByUserID"             = $values[17]
                        "ValidationStatus"           = $values[18]
                        "HumanCreationDate"          = $values[19]
                        "HumanCreatedBy"             = $values[20]
                        "HumanLastUsedDate"          = $values[21]
                        "HumanLastUsedBy"            = $values[22]
                        "HumanLastRetrievedByDate"   = $values[23]
                        "HumanLastRetrievedBy"       = $values[24]
                        "ComponentCreationDate"      = $values[25]
                        "ComponentCreatedBy"         = $values[26]
                        "ComponentLastUsedDate"      = $values[27]
                        "ComponentLastUsedBy"        = $values[28]
                        "ComponentLastRetrievedDate" = $values[29]
                        "ComponentLastRetrievedBy"   = $values[30]
                        "FileCategories"             = $values[31]

                    } | Add-ObjectDetail -TypeName pacli.PoShPACLI.File

Sample Output

Context

I'm getting the list of all accounts in safe with all their attributes, including filecategories and export it as a table.

InconstantRO commented 3 years ago

Also I think it could be possible to normalize it in that way that later you can use ConvertFrom-Csv function, which also supports multi-line attributes and can grab it correctly natively and afterwards you will have parameter contain also multi-line string, instead of single line normalized line, as I did above. I belive that it is enough to replace double quotes " with single ' qote in the CPM Error message Headers are just for example below, h1, h2, etc.. could be replaced by "Filename", "Accessed", etc..

$header = "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "h9", "h10", "h11", "h12", "h13", "h14", "h15", "h16", "h17", "h18", "h19", "h20", "h21", "h22", "h23", "h24", "h25", "h26", "h27", "h28", "h29", "h30", "h31", "h32"
$csvPo = $normalizedoutput | ConvertFrom-Csv -Header $header