Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Earn the coveted Fabric Analytics Engineer certification. 100% off your exam for a limited time only!

Reply
RossJ
Helper I
Helper I

Expression.Error: No podemos convertir el valor null en el tipo Text. Detalles: Valor- Tipo-Tipo

Hola comunidad

Tengo un informe de escritorio que llama a una API web proporcionada por Tenable, un analizador de vulnerabilidades de seguridad, para recuperar una lista de activos de TI y vulnerabilidades asociadas. El informe llama a un punto de conexión para obtener una lista de activos y, a continuación, otros tres puntos de conexión para anexar columnas para las vulnerabilidades identificadas para cada recurso. La vista previa funciona correctamente, pero se produce el siguiente error al aplicar cambios:

Failed to save modifications to the server. Error returned: "OLE DB or ODBC error: [DataSource.Error] Web.Contents failed to get contents from [URL] (500): Internal Server Error."

Usé el método descriped por Reza Rad (2018) para crear dos refrences a mi consulta original, una eliminando filas de error y una manteniendo filas de error:

let
    Source = #"Tenable - Original",
    #"Removed Errors" = Table.RemoveRowsWithErrors(Source)
in
    #"Removed Errors"

Sin embargo, esto no impide que se produzca el error, por lo que este método no debe mover todos los errores. Cuando miro mi referencia donde he guardado las filas de error, veo el siguiente error:

Expression.Error: We cannot convert the value null to type Text.
Details:
    Value=
    Type=Type

Trabajé hacia atrás, e identifiqué que el error está siendo lanzado por el paso "Expandir detalles de vulnerabilidad" en mi consulta:

let
    #"Get JSON object" = fnGetJsonFromTenable("https://cloud.tenable.com/workbenches/assets?date_range=30&filter.0.quality=eq&filter.0.filter=has_plugin_results&filter.0.value=true"),
    #"Drill down list" = #"Get JSON object"[assets],
    #"Convert to table" = Table.FromList(#"Drill down list", Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expand column" = Table.ExpandRecordColumn(#"Convert to table", "Column1",
        {"ipv4", "fqdn", "netbios_name", "operating_system", "has_agent", "id", "last_seen"},
        {"IPv4", "DNS", "NetBIOS", "OS", "Tenable Agent Installed", "Tenable Asset UUID", "Asset Last Seen"}
    ),
    #"Expand IPv4" = Table.ExpandListColumn(#"Expand column", "IPv4"),
    #"Expand DNS" = Table.ExpandListColumn(#"Expand IPv4", "DNS"),
    #"Expand NetBIOS" = Table.ExpandListColumn(#"Expand DNS", "NetBIOS"),
    #"Expand OS" = Table.ExpandListColumn(#"Expand NetBIOS", "OS"),
    #"Get asset location" = Table.AddColumn(#"Expand OS", "Location", each fnGetAssetTagByName([Tenable Asset UUID], "Location")),
    #"Expand location" = Table.ExpandTableColumn(#"Get asset location", "Location", {"Tag Value"}, {"Location"}),
    #"Get asset vulnerabilities" = Table.AddColumn(#"Expand location", "Vulnerabilities", each fnGetAssetVulnerabilities([Tenable Asset UUID])),
    #"Expand vulnerabilities" = Table.ExpandTableColumn(#"Get asset vulnerabilities", "Vulnerabilities", {"Plugin ID", "Plugin Name", "Plugin Family"}),
    #"Change Plugin ID to text" = Table.TransformColumnTypes(#"Expand vulnerabilities",{{"Plugin ID", type text}}),
    #"Get vulnerability details" = Table.AddColumn(#"Change Plugin ID to text", "VulnerabilityDetails", each fnGetAssetVulnerabilityDetails([Tenable Asset UUID], [Plugin ID])),
    #"Expand vulnerability details" = Table.ExpandTableColumn(#"Get vulnerability details", "VulnerabilityDetails", {"Vulnerability First Seen", "Vulnerability Last Seen", "Patch Published", "CVSSv3 Base Score", "CVSSv3 Temporal Score", "CPE", "Exploitable", "Exploit Ease"}),
    #"Change data types" = Table.TransformColumnTypes(#"Expand vulnerability details",{{"IPv4", type text}, {"DNS", type text}, {"NetBIOS", type text}, {"OS", type text}, {"Tenable Agent Installed", type logical}, {"Tenable Asset UUID", type text}, {"Asset Last Seen", type datetime}, {"Location", type text}, {"Plugin Name", type text}, {"Plugin Family", type text}, {"Vulnerability First Seen", type datetime}, {"Vulnerability Last Seen", type datetime}, {"Patch Published", type datetime}, {"CVSSv3 Base Score", type number}, {"CVSSv3 Temporal Score", type number}, {"CPE", type text}, {"Exploitable", type logical}, {"Exploit Ease", type text}})

in
    #"Change data types"

Dado que la vista previa funciona correctamente, no sé qué fila está causando el error y, por lo tanto, cómo resolverlo. Creo que la función personalizada fnGetAssetVulnerabilityDetails llamada en el paso "Obtener detalles de vulnerabilidad" no siempre devuelve una estructura válida, pero no sé cómo solucionarlo. La función se define como:

let
    fnGetAssetVulnerabilityDetails = (assetId as text, pluginId as text) =>
    let
        JsonObject = fnGetJsonFromTenable("https://cloud.tenable.com/workbenches/assets/" & assetId & "/vulnerabilities/" & pluginId & "/info"),
        ConvertToTable = Record.ToTable(JsonObject),
        DeleteNameColumn = Table.RemoveColumns(ConvertToTable, {"Name"}),
        ExpandValue = Table.ExpandRecordColumn(DeleteNameColumn, "Value",
            {"discovery", "plugin_details", "risk_information", "vulnerability_information"},
            {"discovery", "plugin_details", "risk_information", "vulnerability_information"}
        ),
        ExpandDiscovery = Table.ExpandRecordColumn(ExpandValue, "discovery",
            {"seen_first", "seen_last"},
            {"Vulnerability First Seen", "Vulnerability Last Seen"}
        ),
        ExpandPluginDetails = Table.ExpandRecordColumn(ExpandDiscovery, "plugin_details",
            {"publication_date"},
            {"Patch Published"}
        ),
        ExpandRiskInformation = Table.ExpandRecordColumn(ExpandPluginDetails, "risk_information",
            {"cvss3_base_score", "cvss3_temporal_score"},
            {"CVSSv3 Base Score", "CVSSv3 Temporal Score"}
        ),
        ExpandVulnerabilityInformation = Table.ExpandRecordColumn(ExpandRiskInformation, "vulnerability_information",
            {"cpe", "exploit_available", "exploitability_ease"},
            {"CPE", "Exploitable", "Exploit Ease"}
        ),
        ReplaceNullCpe = Table.TransformColumns(ExpandVulnerabilityInformation, {"CPE", each if _ is null then {""} else _}),
        ExtractCpe = Table.TransformColumns(ReplaceNullCpe, {"CPE", each Text.Combine(List.Transform(_, Text.From), ","), type text})
    in
        ExtractCpe 
in
    fnGetAssetVulnerabilityDetails

Para el contexto. la siguiente tabla proporciona datos de ejemplo del informe:

IPv4NetbiosDnselTenable Agent instaladoTenable UUIDActivo última vistaEtiquetas de ubicaciónPlugin IDNombre del pluginFamilia PluginCpeSeveridadPuntuación base de CVSSv3Puntuación temporal CVSSv3ExplotableFacilidad de explotaciónVulnerabilidad vista por primera vezVulnerabilidad vista por última vezParche publicadoFecha
[Redactado][Redactado][Redactado]Microsoft Windows 10 EnterpriseVerdad[Redactado]2020-04-09T11:58:20.342Z 63155Enumeración de ruta de servicio sin comillas de Microsoft WindowsWindows Medio7.87VerdadLas vulnerabilidades están disponibles2019-04-12T15:36:11.283Z2020-04-09T11:58:20.342Z2012-12-05T00:00:00Z28/04/2020

Le agradecería cualquier sugerencia o consejo.

1 ACCEPTED SOLUTION
RossJ
Helper I
Helper I

Me las arreglé para resolver esto. Primero pensé que el error nulo se estaba lanzando dentro de la función personalizada fnGetAssetVulnerabilityDetails, así que intenté desinfectar los nulos antes de concatenar la cadena, pero eso no funcionó. Entonces pensé que el encabezado de la función arrojaba un error nulo, así que cambié la declaración de la función de

 

let fnGetAssetVulnerabilityDetails = (assetId as text, pluginId as text) =>

 

a

 

let fnGetAssetVulnerabilityDetails = (assetId as any, pluginId as any) =>

 

para permitir la entrada de nulos, pero tampoco funcionó.

 

Luego descubrí que el error se estaba generando en mi procedimiento principal al llamar a la función. No entiendo por qué Power BI no estaba pasando parámetros nulos, pero agregó un paso para eliminar los nulos antes de llamar a la función.

 

    #"Change Plugin ID data type" = Table.TransformColumnTypes(#"Expand vulnerabilities",{{"Plugin ID", type text}}),
    #"Replace null Plugin IDs" = Table.TransformColumns(#"Change Plugin ID data type", {"Plugin ID", each if _ is null then "0" else _}),
    #"Get vulnerability details" = Table.AddColumn(#"Replace null Plugin IDs", "VulnerabilityDetails", each fnGetAssetVulnerabilityDetails([Tenable Asset UUID], [Plugin ID])),

 

Espero que esto ayude a alguien más.

View solution in original post

7 REPLIES 7
Syndicate_Admin
Administrator
Administrator

Hola RossJ, ¿puedes compartir cómo lograste conectarte a tenable rest api, con PowerBi en primer lugar? ¿Qué parámetros utilizaste? ¡Gracias!

Hi Stylebender

Escribí una función personalizada llamada fnGetJsonFromTenable:

let
    fnGetJsonFromTenable = (url as text, accessKey as text, secretKey as text) =>
    let
        KeyString = "accessKey=" & accessKey & "; secretKey=" & secretKey & ";",
        Source = Web.Contents(url,
            [
                Headers = [#"X-ApiKeys" = KeyString]
            ]
        ),
        TextString = Text.FromBinary(Source),
        JsonObject = Json.Document(TextString)
    in
        JsonObject
in
    fnGetJsonFromTenable

Entonces lo llamó así:

let
   #"Define API URL" = "https://cloud.tenable.com/assets",
   #"Define access key" = "[redacted]",
   #"Define secret key" = "[redacted]",
   #"Get JSON object" = fnGetJsonFromTenable(#"Define API URL", #"Define access key", #"Define secret key"),
   #"Drill down list" = #"Get JSON object"[assets],
   #"Convert to table" = Table.FromList(#"Drill down list", Splitter.SplitByNothing(), null, null, ExtraValues.Error),
in
   #"Convert to table"

¡Espero que esto ayude!

Roß

@RossJ

Espero que sigas activo en la comunidad y puedas ayudarme con esto: hice exactamente lo que describiste en tu último mensaje, pero siempre tengo un tiempo de espera

aj1973_0-1666894815336.png

¿Podría ser el tamaño de la base de datos? si es así, ¿cómo puede agregar un filtro a su código M para reducir el tamaño?

Gracias

Hola @aj1973

Han pasado dos años desde que estaba trabajando en esto, así que mi memoria es borrosa. Estaba recuperando 23 160 activos de Tenable y el proceso estaba tomando mucho tiempo, así que si tiene más activos que eso, es posible que se esté agotando.

En primera instancia, le sugiero que cambie la solicitud para recuperar un solo activo mediante el punto final https://cloud.tenable.com/workbenches/assets/{asset_id}/info [documento de API]. Eso eliminará los tiempos de espera de conexión de red como el problema.

Si esa prueba de concepto funciona, entonces tiene dos opciones: aumentar el tiempo de espera o disminuir la carga útil.

a) Aumentar el tiempo de espera del servicio. He echado un vistazo y no puedo ver dónde aumentar el tiempo de espera para las llamadas al servicio web. Aquí hay algo de discusión sobre cómo extender Power-BI-service-timeout-limit, pero eso parece estar limitado a las conexiones de base de datos.

b) Disminuir la carga útil. Tenable permite filtros [API doc]. No los he usado, pero creo que podría filtrar por un atributo para reducir el número de registros devueltos. Por ejemplo, recupere activos con dirección IP que comience con 192.168.1.x y, a continuación, itere de 1 a 255. No he probado esto y mis habilidades de PowerBI son limitadas, por lo que no puedo sugerir cómo hacerlo en un bucle.

Buena suerte.

Roß

Hola Ross
Muchas gracias por su respuesta, realmente ayudó, me acercó a una solución.

El problema al que me enfrento en este momento, después de ejecutar la 2ª función, recibo un error: "El acceso al recurso está prohibido"

Si establezco la autentificación en cualquier otra cosa excepto Anonymous, obtengo el siguiente errror:
"El encabezado 'X-ApiKeys' solo se admite cuando se conecta de forma anónima"

¡Su ayuda en este es muy apreciada!

RossJ
Helper I
Helper I

Me las arreglé para resolver esto. Primero pensé que el error nulo se estaba lanzando dentro de la función personalizada fnGetAssetVulnerabilityDetails, así que intenté desinfectar los nulos antes de concatenar la cadena, pero eso no funcionó. Entonces pensé que el encabezado de la función arrojaba un error nulo, así que cambié la declaración de la función de

 

let fnGetAssetVulnerabilityDetails = (assetId as text, pluginId as text) =>

 

a

 

let fnGetAssetVulnerabilityDetails = (assetId as any, pluginId as any) =>

 

para permitir la entrada de nulos, pero tampoco funcionó.

 

Luego descubrí que el error se estaba generando en mi procedimiento principal al llamar a la función. No entiendo por qué Power BI no estaba pasando parámetros nulos, pero agregó un paso para eliminar los nulos antes de llamar a la función.

 

    #"Change Plugin ID data type" = Table.TransformColumnTypes(#"Expand vulnerabilities",{{"Plugin ID", type text}}),
    #"Replace null Plugin IDs" = Table.TransformColumns(#"Change Plugin ID data type", {"Plugin ID", each if _ is null then "0" else _}),
    #"Get vulnerability details" = Table.AddColumn(#"Replace null Plugin IDs", "VulnerabilityDetails", each fnGetAssetVulnerabilityDetails([Tenable Asset UUID], [Plugin ID])),

 

Espero que esto ayude a alguien más.

RossJ
Helper I
Helper I

Me las arreglé para resolver esto. Primero pensé que el error nulo se estaba producendo dentro de la función personalizada fnGetAssetVulnerabilityDetails, así que intenté desinfectar los valores NULL antes de concatenar la cadena, pero eso no funcionó. Entonces pensé que el error nulo estaba siendo lanzado por el encabezado de la función, así que cambié la declaración de función de

let fnGetAssetVulnerabilityDetails = (assetId as text, pluginId as text) =>

Para

let fnGetAssetVulnerabilityDetails = (assetId as any, pluginId as any) =>

para permitir que los nulos entren, pero eso tampoco funcionó.

Luego descubrí que el error se estaba lanzando en mi procedimiento principal mientras se llamaba a la función. No entiendo por qué Power BI no pasaba parámetros nulos, pero agregué un paso para quitar los valores NULL antes de llamar a la función.

    #"Change Plugin ID data type" = Table.TransformColumnTypes(#"Expand vulnerabilities",{{"Plugin ID", type text}}),
    #"Replace null Plugin IDs" = Table.TransformColumns(#"Change Plugin ID data type", {"Plugin ID", each if _ is null then "0" else _}),
    #"Get vulnerability details" = Table.AddColumn(#"Replace null Plugin IDs", "VulnerabilityDetails", each fnGetAssetVulnerabilityDetails([Tenable Asset UUID], [Plugin ID])),

Espero que esto ayude a otra persona.

Helpful resources

Announcements
April AMA free

Microsoft Fabric AMA Livestream

Join us Tuesday, April 09, 9:00 – 10:00 AM PST for a live, expert-led Q&A session on all things Microsoft Fabric!

March Fabric Community Update

Fabric Community Update - March 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors