Functions/Get-RSCSensitiveData/Get-RSCSensitiveDataFiles.ps1

################################################
# Creating the Get-RSCSensitiveDataFiles function
################################################
Function Get-RSCSensitiveDataFiles {

<#
.SYNOPSIS
A Rubrik Security Cloud (RSC) Reporting Module Function returning a list of files with sensitive data for the ObjectID specified.
 
.DESCRIPTION
Makes the required GraphQL API calls to RSC via Invoke-RestMethod to get the data as described, then creates a usable array of the returned information, removing the need for the PowerShell user to understand GraphQL in order to interact with RSC.
.LINK
GraphQL schema reference: https://rubrikinc.github.io/rubrik-api-documentation/schema/reference
 
.PARAMETER ObjectID
A valid ObjectID with sensitive data discovery configured, use Get-RSCSensitiveDataObjects to obtain.
 
.OUTPUTS
Returns an array of all the available information on the GraphQL endpoint in a uniform and usable format.
 
.EXAMPLE
Get-RSCSensitiveDataFiles -ObjectID "403403449-434534-435345-345345"
This example returns a list of all the sensitive files found on the ObjectID specified.
 
.NOTES
Author: Joshua Stenhouse
Date: 05/11/2023
#>

################################################
# Paramater Config
################################################
[CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [string]$ObjectID
    )

# Example: $ObjectSnapshots= Get-RSCObjectSnapshots -ObjectID "$ObjectID"

################################################
# Importing Module & Running Required Functions
################################################
# Importing the module is it needs other modules
Import-Module RSCReporting
# Checking connectivity, exiting function with error if not connected
Test-RSCConnection
# Getting objects
$RSCSensitiveDataObjects = Get-RSCSensitiveDataObjectAnalyzerHits
# Selecting object based on ID
$RSCSensitiveDataObject = $RSCSensitiveDataObjects | Where-Object {$_.ObjectID -eq $ObjectID} | Select -First 1
# Selecting snapshot & object info
$ObjectURL = $RSCSensitiveDataObject.URL
$ObjectName = $RSCSensitiveDataObject.Object
$ObjectType = $RSCSensitiveDataObject.ObjectType
$SnapshotID = $RSCSensitiveDataObject.SnapshotID
# If nulls, breaking
IF($RSCSensitiveDataObjects -eq $null)
{
Write-Error "ERROR: Object not found on Get-RSCSensitiveDataObjects, check the ObjectID and try again.."
Start-Sleep 2
Break
}
IF($SnapshotID -eq $null)
{
Write-Error "ERROR: SnapshotID not found on Get-RSCSensitiveDataObjects, check the Object has a SnapshotID and try again.."
Start-Sleep 2
Break
}
Write-Host "SnapshotID: $SnapshotID"
################################################
# Running Main Function
################################################
$ObjectFileList = @() 
# Creating array for path
$snappablePaths = [System.Collections.ArrayList]@()
$Object = New-Object PSObject
$Object | Add-Member -MemberType NoteProperty -Name "snappableFid" -Value $ObjectID
$Object | Add-Member -MemberType NoteProperty -Name "stdPath" -Value ""
$snappablePaths.Add($Object) | Out-Null
# Creating graphQL
$RSCGraphQL = @{"operationName" = "ObjectFilesQuery";

"variables" = @{
"snappableFid" = "$ObjectID"
"snapshotFid" = "$SnapshotID"
"first" = 50
"timezone" = "America/New_York"
"filters" = @{
        "fileType" = "HITS"
        "searchText" = ""
        "whitelistEnabled" = $true
        }
"sort" = @{
        "sortOrder" = "DESC"
        "sortBy" = "HITS"
}
};

"query" = "query ObjectFilesQuery(`$first: Int!, `$after: String, `$snappableFid: String!, `$snapshotFid: String!, `$filters: ListFileResultFiltersInput, `$sort: FileResultSortInput, `$timezone: String!) {
  policyObj(snappableFid: `$snappableFid, snapshotFid: `$snapshotFid) {
  id: snapshotFid
  fileResultConnection(first: `$first, after: `$after, filter: `$filters, sort: `$sort, timezone: `$timezone) {
      edges {
        cursor
        node {
          ...DiscoveryFileFragment
          __typename
        }
        __typename
      }
      pageInfo {
        startCursor
        endCursor
        hasNextPage
        hasPreviousPage
        __typename
      }
      hasLatestData
      __typename
    }
    __typename
  }
}
 
fragment DiscoveryFileFragment on FileResult {
  nativePath
  stdPath
  filename
  mode
  size
  lastAccessTime
  lastModifiedTime
  directory
  numDescendantFiles
  numDescendantErrorFiles
  numDescendantSkippedExtFiles
  numDescendantSkippedSizeFiles
  errorCode
  hits {
    totalHits
    violations
    violationsDelta
    totalHitsDelta
    __typename
  }
  filesWithHits {
    totalHits
    violations
    __typename
  }
  openAccessFilesWithHits {
    totalHits
    violations
    __typename
  }
  staleFilesWithHits {
    totalHits
    violations
    __typename
  }
  analyzerGroupResults {
    ...AnalyzerGroupResultFragment
    __typename
  }
  sensitiveFiles {
    highRiskFileCount {
      totalCount
      violatedCount
      __typename
    }
    mediumRiskFileCount {
      totalCount
      violatedCount
      __typename
    }
    lowRiskFileCount {
      totalCount
      violatedCount
      __typename
    }
    __typename
  }
  openAccessType
  stalenessType
  numActivities
  numActivitiesDelta
  __typename
}
 
fragment AnalyzerGroupResultFragment on AnalyzerGroupResult {
  analyzerGroup {
    groupType
    id
    name
    __typename
  }
  analyzerResults {
    hits {
      totalHits
      violations
      __typename
    }
    analyzer {
      id
      name
      analyzerType
      __typename
    }
    __typename
  }
  hits {
    totalHits
    violations
    violationsDelta
    totalHitsDelta
    __typename
  }
  __typename
}"

}
# Converting to JSON
$RSCGraphQLJSON = $RSCGraphQL | ConvertTo-Json -Depth 32
# Converting back to PS object for editing of variables
$RSCGraphQLJSONObject = $RSCGraphQLJSON | ConvertFrom-Json
# Adding variables specified
$RSCGraphQLJSONObject.variables.filters | Add-Member -MemberType NoteProperty "snappablePaths" -Value $snappablePaths
################################################
# API Call To RSC GraphQL URI
################################################
# Querying API
$ObjectFilesResponse = Invoke-RestMethod -Method POST -Uri $RSCGraphqlURL -Body $($RSCGraphQLJSONObject | ConvertTo-JSON -Depth 20) -Headers $RSCSessionHeader
# Setting variable
$ObjectFileList += $ObjectFilesResponse.data.policyObj.fileResultConnection.edges.node
# Logging iterations (as it may take a while)
$FileCount = 0
Write-host "GettingFiles: 0-50"
# Getting all results from paginations
While ($ObjectFilesResponse.data.policyObj.fileResultConnection.pageInfo.hasNextPage) 
{
# Logging
$FileCount = $FileCount + 50; $FileCountNext = $FileCount + 50
Write-host "GettingFiles: $FileCount-$FileCountNext"
# Getting next set
$RSCGraphQLJSONObject.variables | Add-Member -MemberType NoteProperty "after" -Value $ObjectFilesResponse.data.policyObj.fileResultConnection.pageInfo.endCursor -Force
$ObjectFilesResponse = Invoke-RestMethod -Method POST -Uri $RSCGraphqlURL -Body $($RSCGraphQLJSONObject | ConvertTo-JSON -Depth 20) -Headers $RSCSessionHeader
$ObjectFileList += $ObjectFilesResponse.data.policyObj.fileResultConnection.edges.node
}
# Logging
$ObjectFileListCount = $ObjectFileList | Measure-Object | Select-Object -ExpandProperty Count
Write-Host "--------------------------
FilesWithHits: $ObjectFileListCount
Processing, this may take a few minutes..."

################################################
# Processing List
################################################
# Creating array
$ObjectFiles = [System.Collections.ArrayList]@()
# Processing snapshots
ForEach ($ObjectFile in $ObjectFileList)
{
# Setting variables
$FilePath = $ObjectFile.nativePath
$FileName = $ObjectFile.filename
$FileSizeBytes = $ObjectFile.size
$LastAccessTimeEPOCH = $ObjectFile.lastAccessTime
$LastModifiedTimeEPOCH = $ObjectFile.lastModifiedTime
$TotalHits = $ObjectFile.hits.totalHits
$Violations = $ObjectFile.hits.violations
$PermittedHits = $ObjectFile.hits.permittedHits
$ViolationsDelta = $ObjectFile.hits.violationsDelta
$TotalHitsDelta = $ObjectFile.hits.totalHitsDelta
$OpenAccessType = $ObjectFile.openAccessType
$StalenessType = $ObjectFile.stalenessType
# Converting times
IF($LastAccessTimeEPOCH -ne $null){$LastAccessTime = (Get-Date -Date "01-01-1970") + ([System.TimeSpan]::FromSeconds(($LastAccessTimeEPOCH)))}ELSE{$LastAccessTime = $null}
IF($LastModifiedTimeEPOCH -ne $null){$LastModifiedTime = (Get-Date -Date "01-01-1970") + ([System.TimeSpan]::FromSeconds(($LastModifiedTimeEPOCH)))}ELSE{$LastModifiedTime = $null}
# Converting size
$FileSizeKB = $FileSizeBytes / 1000
$FileSizeKB = [Math]::Round($FileSizeKB)
$FileSizeMB = $FileSizeBytes / 1000 / 1000
$FileSizeMB = [Math]::Round($FileSizeMB,2)
# Analyzers
$AnalyzerGroupResults = $ObjectFile.analyzerGroupResults
$TotalAnalyzersCount = $AnalyzerGroupResults | Measure-Object | Select-Object -ExpandProperty Count
$AnalyzersWithHits = $AnalyzerGroupResults | Where-Object {$_.hits.totalhits -gt 0}
$AnalyzersWithNoHits = $AnalyzerGroupResults | Where-Object {$_.hits.totalhits -eq 0}
$AnalyzersWithHitsCount = $AnalyzersWithHits | Measure-Object | Select-Object -ExpandProperty Count
$AnalyzersWithNoHitsCount = $AnalyzersWithNoHits | Measure-Object | Select-Object -ExpandProperty Count
# Getting analyzer names with hits
$AnalyzersWithHitsNames = $AnalyzersWithHits | Select-Object -ExpandProperty analyzerGroup | Select-Object -ExpandProperty Name
$AnalyzersWithHitsIDs = $AnalyzersWithHits | Select-Object -ExpandProperty analyzerGroup | Select-Object -ExpandProperty id
# Adding to array
$Object = New-Object PSObject
$Object | Add-Member -MemberType NoteProperty -Name "FilePath" -Value $FilePath
$Object | Add-Member -MemberType NoteProperty -Name "FileName" -Value $FileName
$Object | Add-Member -MemberType NoteProperty -Name "SizeBytes" -Value $FileSizeBytes
$Object | Add-Member -MemberType NoteProperty -Name "SizeKB" -Value $FileSizeKB
$Object | Add-Member -MemberType NoteProperty -Name "SizeMB" -Value $FileSizeMB
$Object | Add-Member -MemberType NoteProperty -Name "TotalHits" -Value $TotalHits
$Object | Add-Member -MemberType NoteProperty -Name "Violations" -Value $Violations
$Object | Add-Member -MemberType NoteProperty -Name "PermittedHits" -Value $PermittedHits
$Object | Add-Member -MemberType NoteProperty -Name "TotalHitsDelta" -Value $TotalHitsDelta
$Object | Add-Member -MemberType NoteProperty -Name "ViolationsDelta" -Value $ViolationsDelta
$Object | Add-Member -MemberType NoteProperty -Name "OpenAccessType" -Value $OpenAccessType
$Object | Add-Member -MemberType NoteProperty -Name "StalenessType" -Value $StalenessType
$Object | Add-Member -MemberType NoteProperty -Name "LastModified" -Value $LastModifiedTime
$Object | Add-Member -MemberType NoteProperty -Name "LastAccessed" -Value $LastAccessTime
$Object | Add-Member -MemberType NoteProperty -Name "TotalAnalyzers" -Value $TotalAnalyzersCount
$Object | Add-Member -MemberType NoteProperty -Name "AnalyzersWithHits" -Value $AnalyzersWithHitsCount
$Object | Add-Member -MemberType NoteProperty -Name "AnalyzersWithoutHits" -Value $AnalyzersWithNoHitsCount
$Object | Add-Member -MemberType NoteProperty -Name "AnalyzersWithHitsNames" -Value $AnalyzersWithHitsNames
$Object | Add-Member -MemberType NoteProperty -Name "AnalyzersWithHitsIDs" -Value $AnalyzersWithHitsIDs
$Object | Add-Member -MemberType NoteProperty -Name "Object" -Value $ObjectName
$Object | Add-Member -MemberType NoteProperty -Name "ObjectID" -Value $ObjectID
$Object | Add-Member -MemberType NoteProperty -Name "Type" -Value $ObjectType
$Object | Add-Member -MemberType NoteProperty -Name "SnapshotID" -Value $SnapshotID
$Object | Add-Member -MemberType NoteProperty -Name "URL" -Value $ObjectURL
$ObjectFiles.Add($Object) | Out-Null
# End of for each file below
}
# End of for each file above

# Sorting by hits
$ObjectFiles = $ObjectFiles | Sort-Object TotalHits -Descending

# Found a bug whereby it doesn't let me export to CSV unless I select them again by the count
$ObjectFiles = $ObjectFiles | Select-Object -First $ObjectFileListCount

# Returning Result
Return $ObjectFiles
}