@@ -35,6 +35,129 @@ enum EntryFormats {
3535 markdown
3636}
3737
38+ <#
39+ . DESCRIPTION
40+ Analyze a PS dict-like object recursively and return any paths that reference to a parent object.
41+
42+ This function employs recursion to traverse through the object hierarchy, keeping track of visited objects to detect
43+ circular references. The function stops recursion when a specified maximum depth or the end of the hierarchy is reached.
44+ If a circular reference is found, it returns the path leading to it.
45+
46+ . PARAMETER obj
47+ The object to analyze.
48+
49+ . PARAMETER visited
50+ A dict containing the parent objects to check against. Should be left empty outside the recursion.
51+
52+ . PARAMETER path
53+ The path to the object being analyzed. Should be left empty outside the recursion.
54+
55+ . PARAMETER depth
56+ The amount of recursive calls to the function up to the current call. Should be 1 outside the recursion.
57+
58+ . PARAMETER maxDepth
59+ The maximum amount of recursive function calls, defaults to 20.
60+
61+ . OUTPUTS
62+ A list of strings of the self referencing paths inside the provided object.
63+ #>
64+ function Get-SelfReferencingPaths ($obj , $visited = @ {}, $path = @ (), $depth = 1 , $maxDepth = 20 ) {
65+ if ($depth -gt $maxDepth ) {
66+ # Stop recursion when max depth is reached
67+ return @ ()
68+ }
69+
70+ $selfReferencingPaths = @ ()
71+ # If the object has properties (has children that can point back to it), and is not null.
72+ # Get-Member function will return an error if the member type doesn't exist but here we are setting
73+ # SilentlyContinue instead.
74+ if (($obj | Get-Member - MemberType Properties - ErrorAction SilentlyContinue)) {
75+ if ($visited.ContainsKey ($obj )) {
76+ # Circular reference detected
77+ if (-not ($path -like " *SyncRoot*" )) {
78+ # SyncRoot is allowed to be self-referencing
79+ return $path
80+ }
81+ return @ ()
82+ }
83+
84+ # Mark the object as visited
85+ $visited [$obj ] = $true
86+
87+ foreach ($property in $obj.PSObject.Properties ) {
88+ $propertyValue = $property.Value
89+ $propertyPath = " $ ( $path ) .$ ( $property.Name ) "
90+ if (-not $path ) {
91+ $propertyPath = " $ ( $property.Name ) "
92+ }
93+
94+ # Recursively process complex object
95+ $nestedPaths = Get-SelfReferencingPaths - obj $propertyValue - visited $visited - path $propertyPath - depth ($depth + 1 ) - maxDepth $maxDepth
96+ $selfReferencingPaths += $nestedPaths
97+ }
98+
99+ # Remove the object from visited list after getting all its children
100+ $visited.Remove ($obj )
101+ }
102+
103+ return $selfReferencingPaths
104+ }
105+
106+ <#
107+ . DESCRIPTION
108+ Remove any circular references from a PS dict-like object.
109+
110+ This function gets the circular referencing paths of the object using Get-SelfReferencingPaths,
111+ It then transverses through the object properties until the circular referencing parent node is reached.
112+ It removes the circular referencing node from the parent node and transverses back to update each parent node with the
113+ change.
114+
115+ . PARAMETER obj
116+ The object to remove self references from.
117+
118+ . OUTPUTS
119+ The updated object, containing no self references.
120+ #>
121+ function Remove-SelfReferences ($obj ) {
122+
123+ try {
124+ # Get self referencing paths
125+ $selfReferencingPaths = Get-SelfReferencingPaths - obj $obj
126+
127+ foreach ($path in $selfReferencingPaths ) {
128+ $properties = $path -split ' \.'
129+ $propertyName = $properties [-1 ]
130+ $parentPath = $properties [0 .. ($properties.Count - 2 )]
131+
132+ $parentObject = $obj
133+ $parentObjects = @ ()
134+ # Get object at the end of path
135+ foreach ($prop in $parentPath ) {
136+ $parentObjects += $parentObject
137+ $parentObject = $parentObject .($prop )
138+ }
139+
140+ # Update the object
141+ $currentObject = $parentObject | Select-Object - ExcludeProperty $propertyName
142+
143+ # Update back all its parents
144+ for ($i = $parentObjects.Count - 1 ; $i -ge 0 ; $i -- ) {
145+ $parentObject = $parentObjects [$i ]
146+ $propName = $properties [$i ]
147+ $parentObject .$propName = $currentObject
148+ $currentObject = $parentObject
149+ }
150+
151+ }
152+
153+ return $obj
154+
155+ } catch {
156+ # Return to default behaviour if errors were encountered.
157+ return $obj
158+ }
159+ }
160+
38161# Demist Object Class for communicating with the Demisto Server
39162[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute (' PSAvoidGlobalVars' , ' ' , Justification = ' use of global:DemistoServerRequest' )]
40163class DemistoObject {
@@ -238,7 +361,7 @@ class DemistoObject {
238361 if ( -not $this.IsIntegration ) {
239362 throw " Method not supported"
240363 }
241- $Incidents = $Incidents | ConvertTo-Json - Depth 6
364+ $Incidents = $Incidents | ConvertTo-Json - Depth 6 - AsArray
242365 return $this.Results (@ {Type = 1 ; Contents = $Incidents ; ContentsFormat = " json" })
243366 }
244367
@@ -393,10 +516,22 @@ The outputs that will be returned to playbook/investigation context (optional)
393516If not provided then will be equal to outputs. usually is the original
394517raw response from the 3rd party service (optional)
395518
519+ . PARAMETER RemoveSelfRefs
520+ If true, will remove self references in RawResponse and Outputs objects before conversion to json.
521+
396522. OUTPUTS
397523The entry object returned to the server
398524#>
399- function ReturnOutputs ([string ]$ReadableOutput , [object ]$Outputs , [object ]$RawResponse ) {
525+ function ReturnOutputs ([string ]$ReadableOutput , [object ]$Outputs , [object ]$RawResponse ,
526+ [Parameter (Mandatory = $false )]
527+ [bool ]$RemoveSelfRefs = $true ) {
528+
529+ if ($RemoveSelfRefs ) {
530+ # Remove circular references before converting to json.
531+ $RawResponse = Remove-SelfReferences $RawResponse
532+ $Outputs = Remove-SelfReferences $Outputs
533+ }
534+
400535 $entry = @ {
401536 Type = [EntryTypes ]::note;
402537 ContentsFormat = [EntryFormats ]::json.ToString();
0 commit comments