¿Ya eres cliente de NinjaOne? Inicia sesión para ver más guías y las últimas actualizaciones.

Script personalizado: Importador de vulnerabilidades

Tema

Este script importa un archivo CSV con vulnerabilidades exportadas desde diversas herramientas y plataformas de análisis de vulnerabilidades.

Entorno

NinjaOne Automation

Descripción

Para utilizar este script, guárdelo como un archivo por lotes y cárguelo en su biblioteca de scripts. A continuación, podrá ejecutarlo según sea necesario desde el botón de reproducción de la página de detalles del dispositivo o desde la vista de búsqueda de dispositivos. Consulte Automatizar el proceso de importación de vulnerabilidades para obtener instrucciones y recursos.

<#
.SYNOPSIS
Este script se utiliza para importar un archivo CSV de vulnerabilidades exportado desde diversas herramientas y plataformas de análisis de vulnerabilidades.
.DESCRIPTION
Este script se utiliza para importar un archivo CSV de vulnerabilidades exportado desde diversas herramientas y plataformas de análisis de vulnerabilidades.
Una vez que el archivo se ha importado correctamente, se eliminará el archivo CSV. Si el CSV no se puede importar debido a un problema
con el CSV, se añadirá «FAILED_» al nombre del CSV. Si la llamada de importación inicial se realiza correctamente, pero las comprobaciones de estado posteriores
fallan, se añadirá «UNKNOWN_» al nombre del CSV. En ese momento, deberá comprobar el estado en la interfaz de usuario de NinjaOne.
# ---------------------------------------------------------------
# Autor: Mark Giordano
# Fecha: 03/05/2025
# Descripción: Vulnerabilidad de NinjaOne Importación y registro
# ---------------------------------------------------------------
.NOTES
Las URL de la API estándar son:
app.ninjarmm.com
eu.ninjarmm.com
oc.ninjarmm.com
ca.ninjarmm.com
us2.ninjarmm.com
Si desea configurar esta tarea como recurrente en NinjaOne, se recomienda hacerlo en un dispositivo seguro que no se utilice para fines cotidianos.
Las credenciales deben almacenarse en campos personalizados seguros y recuperarse desde allí.
Consulte la configuración recomendada por Luke Whitelock (Introducción) aquí: https://docs.mspp.io/ninjaone/getting-started
#>
<# Descomente esta sección si planea extraer sus credenciales API de los campos personalizados seguros de NinjaOne
$ClientID = Ninja-Property-Get NinjaOneAPIClientID
$ClientSecret = Ninja-Property-Get NinjaOneAPISecret
#>
# Ajuste BaseURL a la instancia aplicable #
$BaseURL = 'ca.ninjarmm.com'
# Especificar el ID del grupo de análisis #
$ScanGroupID = 'INTRODUZCA EL NÚMERO DE ID DEL GRUPO DE ANÁLISIS'
# Directorio de los archivos CSV para importar #
$PathtoCSV = "$env:SystemDrivetemp"
# Patrón inicial de los nombres de los archivos CSV que deben coincidir #
$CSVName = 'VulnExport_'
## Configuración de la función ##
function Get-NinjaToken {
$Headers = @{
"Content-Type" = "application/x-www-form-urlencoded"
}
$Scope = "monitoring management offline_access"
if ([string]::IsNullOrWhiteSpace($ClientID) -or [string]::IsNullOrWhiteSpace($ClientSecret)) {
if ($($PSVersionTable.PSVersion -lt [version]'7.0')) {
$ClientID = Read-Host -Prompt "ClientID"
$ClientSecret = Read-Host -Prompt "ClientSecret"
}
else {
$ClientID = Read-Host -MaskInput "ClientID"
$ClientSecret = Read-Host -MaskInput "ClientSecret"
}
}
$GrantType = 'client_credentials'
$params = @{
Uri = "https://$($BaseURL)/ws/oauth/token"
Method = 'POST'
Body = "grant_type=$GrantType&client_id=$ClientID&client_secret=$ClientSecret&scope=$Scope"
}
$Response = Invoke-RestMethod @Params -Headers $Headers
return $Response.access_token
}
function Get-BaseSettings {
[CmdletBinding()]
param (
[Parameter()]
[String]$Method = 'Get',
[Parameter()]
[String]$Body,
[switch]$Paginate,
[string]$After,
[string]$ContentType
)
if ($Paginate) {
$URL = "https://$($BaseURL)/api/v2/$($Request)?pageSize=$($PageSize)&after=$($After)"
}
else {
$URL = "https://$($BaseURL)/api/v2/$($Request)"
}
$Params = @{
Uri = "$URL"
Method = "$Method"
Headers = @{
'Authorization' = 'Bearer {0}' -f "$Token"
"Content-Type" = 'application/json'
}
}
if ($Body) {
$Params['Body'] = $Body
}
return $Params
}
function Get-VulScanGroups {
[CmdletBinding()]
param (
[Parameter()]
[int]$SGID
)
if ($SGID) {
$Request = "/vulnerability/scan-groups/$SGID"
}
else {
$Request = '/vulnerability/scan-groups'
}
$Params = Get-BaseSettings
try {
$ScanGroupRaw = Invoke-WebRequest @Params
return $ScanGroupRaw.Content | ConvertFrom-Json
}
catch {
return $false
}
}
function New-VulScanImport {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[int]$SGID,
[Parameter(Mandatory = $true)]
[string]$CSV
)
if ($($PSVersionTable.PSVersion -ge [version]'7.0')) {
$Request = "vulnerability/scan-groups/$SGID/upload"
$Params = Get-BaseSettings -Method 'Post'
$Form = @{
"csv" = Get-Item $CSV
}
try {
$Response = Invoke-WebRequest @Params -Form $Form
Write-LogEntry -Message ($Response.Content | Out-String)
}
catch {
Write-LogEntry -Message 'Failed to import CSV.'
Write-LogEntry -Message "$($_.Exception.Message)"
exit 1
}
}
else {
$APIURL = "https://$BaseUrl/api/v2/vulnerability/scan-groups/$SGID/upload"
Add-Type -AssemblyName System.Net.Http
$HTTPClient = New-Object System.Net.Http.HttpClient
$HTTPClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $Token)
# Crear el contenido del formulario multiparte
$MPFormData = New-Object System.Net.Http.MultipartFormDataContent
# Abrir el archivo CSV como un flujo y preparar el StreamContent
$FileStream = [System.IO.File]::OpenRead($CSV)
$FileName = [System.IO.Path]::GetFileName($CSV)
$StreamContent = New-Object System.Net.Http.StreamContent($FileStream)
$StreamContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("text/csv")
# Añade el contenido del archivo CSV con el nombre de campo del formulario «csv»
$MPFormData.Add($StreamContent, "csv", $FileName)
# Publicar el contenido multiparte y esperar el resultado
$APICall = $HTTPClient.PostAsync($APIURL, $MPFormData)
$APICall.Wait()
$Response = $APICall.Result
# Leer el contenido de la respuesta
$APIResponse = $Response.Content.ReadAsStringAsync()
$APIResponse.Wait()
if (!($Response.IsSuccessStatusCode)) {
Write-LogEntry -Message 'Failed to import CSV.'
Write-LogEntry -Message ($APIResponse.Result | Out-String)
$FileStream.Close()
$HTTPClient.Dispose()
exit 1
}
Write-LogEntry -Message ($APIResponse.Result | Out-String)
$FileStream.Close()
$HTTPClient.Dispose()
}
}
function Write-LogEntry {
param (
[Parameter(Mandatory = $true)]
[string]$Message
)
$LogPath = "$PathtoCSVNinjaOneVulnerabiltyImport.log"
$TimeStamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Add-Content -Path $LogPath -Value "$TimeStamp $Message"
Write-Host "$TimeStamp $Message"
}
## Fin de la configuración de la función ##
$Token = Get-NinjaToken
if (!(Test-Path $PathtoCSV)) {
Write-LogEntry -Message 'La ruta introducida a los archivos CSV no existe. Salir.'
exit 1
}
$CSVFiles = Get-ChildItem $PathtoCSV | Where-Object { $_.Extension -eq '.csv' -and $_.Name -match "^$CSVName" }
if (!($CSVFiles)) {
Write-LogEntry -Message 'No hay ningún archivo CSV para importar. Salir.'
exit 0
}
foreach ($CSV in $CSVFiles) {
Write-LogEntry -Message "CSV encontrado: $($CSV.Name)"
Write-LogEntry -Message "Importando a ScanGroup $ScanGroupID"
try {
New-VulScanImport -SGID $ScanGroupID -CSV $($CSV.FullName)
}
catch {
Write-LogEntry -Message 'Error al importar csv.'
Write-LogEntry -Message ($_.Exception.Message)
exit 1
}
$ImportStatus = (Get-VulScanGroups -SGID $ScanGroupID).Status
if (!($ImportStatus)) {
Write-LogEntry -Message 'No se puede recuperar el estado de la importación, pero es posible que la importación se haya realizado correctamente.'
Write-LogEntry -Message 'Compruebe el estado de la importación de vulnerabilidades en la interfaz de usuario de NinjaOne.'
Write-LogEntry -Message 'Añadiendo el nombre del archivo CSV con UNKNOWN_'
try {
Rename-Item -Path $($CSV.FullName) "UNKNOWN_$($CSV.Name)" -ErrorAction Stop
Write-LogEntry -Message "Renombrado correctamente como UNKNOWN_$($CSV.Name)."
exit 1
}
catch {
Write-LogEntry -Message "Failed to rename CSV."
exit 1
}
}
while ($ImportStatus -ne 'Complete') {
Write-LogEntry -Message "Import Status: $ImportStatus"
Start-Sleep 10
$ImportStatus = (Get-VulScanGroups -SGID $ScanGroupID).Status
}
if ($ImportStatus -eq 'Complete' ) {
Write-LogEntry -Message "Estado de la importación: $ImportStatus"
Write-LogEntry -Message "Importación correcta"
Write-LogEntry -Message "Eliminando $($CSV.Name)..."
try {
Remove-Item -Path $($CSV.FullName) -Force -ErrorAction Stop
Write-LogEntry -Message "Eliminado correctamente."
exit 0
}
catch {
Write-LogEntry -Message "No se ha podido eliminar el CSV."
exit 1
}
}
else {
Write-LogEntry -Message "Estado de la importación: $ImportStatus"
Write-LogEntry -Message "¡Error en la importación!"
Write-LogEntry -Message "Renaming $($CSV.Name)..."
try {
Rename-Item -Path $($CSV.FullName) "FAILED_$($CSV.Name)" -ErrorAction Stop
Write-LogEntry -Message "Successfully renamed to FAILED_$($CSV.Name)."
exit 1
}
catch {
Write-LogEntry -Message "No se pudo renombrar el CSV."
exit 1
}
}
}

Recursos adicionales

Automatización del proceso de importación de vulnerabilidades

FAQ

Próximos pasos