n00b
n00b

Reputation: 57

How to determine if an array is present in JQ

Hi so I have a curl command for checking some data on an api, then using jq to parse the data for only relevant information.

The issue is some of the data contains a certain class known as .result.CVE_Items[].configurations.nodes[].children[] but others do not have it. So I am trying to write an if, elif and else statement that will parse the data. I am trying to figure out how to place an elif statement to check if this class is present .result.CVE_Items[].configurations.nodes[].children[] and if it is present for it to query the data.

Below are the two commands where one contains the class and the other one doesn't contain the class and brings an error:


# First command works: has the class 

curl -s https://services.nvd.nist.gov/rest/json/cve/1.0/CVE-2020-12389 | jq -r 'if .totalResults == 1 then . | {CVE: .result.CVE_Items[].cve.CVE_data_meta.ID, Description: .result.CVE_Items[].cve.description.description_data[].value, "CVSS V3 Score": .result.CVE_Items[].impact.baseMetricV3.cvssV3.baseSeverity, "Attack Vector": .result.CVE_Items[].impact.baseMetricV3.cvssV3.attackVector, "Privileges Required": .result.CVE_Items[].impact.baseMetricV3.cvssV3.privilegesRequired, "User Interaction": .result.CVE_Items[].impact.baseMetricV3.cvssV3.userInteraction, "Impact Score": .result.CVE_Items[].impact.baseMetricV3.impactScore, "Products Affected": [.result.CVE_Items[].configurations.nodes[].children[].cpe_match[].cpe23Uri]} else . | {Results: .message} end'

Output:

{
  "CVE": "CVE-2020-12389",
  "Description": "The Firefox content processes did not sufficiently lockdown access control which could result in a sandbox escape. *Note: this issue only affects Firefox on Windows operating systems.*. This vulnerability affects Firefox ESR < 68.8 and Firefox < 76.",
  "CVSS V3 Score": "CRITICAL",
  "Attack Vector": "NETWORK",
  "Privileges Required": "NONE",
  "User Interaction": "NONE",
  "Impact Score": 6,
  "Products Affected": [
    "cpe:2.3:a:mozilla:firefox:*:*:*:*:*:*:*:*",
    "cpe:2.3:a:mozilla:firefox_esr:*:*:*:*:*:*:*:*",
    "cpe:2.3:o:microsoft:windows:-:*:*:*:*:*:*:*"
  ]
}



# Second command as the above but different data output and doesn't contain the class hence errors out.

curl -s https://services.nvd.nist.gov/rest/json/cve/1.0/CVE-2020-0796 | jq -r 'if .totalResults == 1 then . | {CVE: .result.CVE_Items[].cve.CVE_data_meta.ID, Description: .result.CVE_Items[].cve.description.description_data[].value, "CVSS V3 Score": .result.CVE_Items[].impact.baseMetricV3.cvssV3.baseSeverity, "Attack Vector": .result.CVE_Items[].impact.baseMetricV3.cvssV3.attackVector, "Privileges Required": .result.CVE_Items[].impact.baseMetricV3.cvssV3.privilegesRequired, "User Interaction": .result.CVE_Items[].impact.baseMetricV3.cvssV3.userInteraction, "Impact Score": .result.CVE_Items[].impact.baseMetricV3.impactScore, "Products Affected": [.result.CVE_Items[].configurations.nodes[].children[].cpe_match[].cpe23Uri]} else . | {Results: .message} end'

Output:

jq: error (at <stdin>:0): Cannot iterate over null (null)

# Error is mainly at the Products affected .... when you remove .children[] it works


curl -s https://services.nvd.nist.gov/rest/json/cve/1.0/CVE-2020-0796 | jq -r 'if .totalResults == 1 then . | {CVE: .result.CVE_Items[].cve.CVE_data_meta.ID, Description: .result.CVE_Items[].cve.description.description_data[].value, "CVSS V3 Score": .result.CVE_Items[].impact.baseMetricV3.cvssV3.baseSeverity, "Attack Vector": .result.CVE_Items[].impact.baseMetricV3.cvssV3.attackVector, "Privileges Required": .result.CVE_Items[].impact.baseMetricV3.cvssV3.privilegesRequired, "User Interaction": .result.CVE_Items[].impact.baseMetricV3.cvssV3.userInteraction, "Impact Score": .result.CVE_Items[].impact.baseMetricV3.impactScore, "Products Affected": [.result.CVE_Items[].configurations.nodes[].cpe_match[].cpe23Uri]} else . | {Results: .message} end'

Output:

{
  "CVE": "CVE-2020-0796",
  "Description": "A remote code execution vulnerability exists in the way that the Microsoft Server Message Block 3.1.1 (SMBv3) protocol handles certain requests, aka 'Windows SMBv3 Client/Server Remote Code Execution Vulnerability'.",
  "CVSS V3 Score": "CRITICAL",
  "Attack Vector": "NETWORK",
  "Privileges Required": "NONE",
  "User Interaction": "NONE",
  "Impact Score": 6,
  "Products Affected": [
    "cpe:2.3:o:microsoft:windows_10:1903:*:*:*:*:*:*:*",
    "cpe:2.3:o:microsoft:windows_10:1909:*:*:*:*:*:*:*",
    "cpe:2.3:o:microsoft:windows_server_2016:1903:*:*:*:*:*:*:*",
    "cpe:2.3:o:microsoft:windows_server_2016:1909:*:*:*:*:*:*:*"
  ]
}


I have tried has("children") and has("children[]") but nothing has worked and I really don't have a clue, I have checked all the github issues raised for checking an element before parsing and also did a search on stackoverflow. I have also checked the docs. I would love it to be in one command where it first check if totalResults == 1 and has(children) == false to query the information elif totalResults == 1 and has(children) == true to query the information else to print out the error message which means the data is not available.

Kindly assist

thanks.

\n00b

Upvotes: 0

Views: 506

Answers (2)

n00b
n00b

Reputation: 57

So @peak gave me some ideas on how to solve this... this code solves it.

curl -s https://services.nvd.nist.gov/rest/json/cve/1.0/CVE-2020-11066 
| jq -r 'if .totalResults == 1 then .result.CVE_Items[] 
| .cve.CVE_data_meta.ID as $ID 
| .cve.description.description_data[].value as $value 
| .configurations.nodes[] as $node 
| if $node | (type == "object" and has("children")) 
then .impact.baseMetricV3 | 
{CVE: $ID, Description: $value, 
"CVSS V3 Score": .cvssV3.baseSeverity, 
"Attack Vector": .cvssV3.attackVector, 
"Privileges Required": .cvssV3.privilegesRequired, 
"User Interaction": .cvssV3.userInteraction, 
"Impact Score": .impactScore, 
"Products Affected": [$node.children[].cpe_match[].cpe23Uri]} 
else .impact.baseMetricV3 | 
{CVE: $ID, Description: $value, 
"CVSS V3 Score": .cvssV3.baseSeverity, 
"Attack Vector": .cvssV3.attackVector, 
"Privileges Required": .cvssV3.privilegesRequired, 
"User Interaction": .cvssV3.userInteraction, 
"Impact Score": .impactScore, 
"Products Affected": [$node.cpe_match[].cpe23Uri]} 
end else . | {Results: .message} end'

So basically if you follow the above answer, @peak showed us how to place an if statement to check if the object class children is present inside an if statement. The else statement fixes our issue and we are good to go.

Upvotes: 1

peak
peak

Reputation: 117017

A glib answer would be to say to use the following if/then/else structure:

if .result.CVE_Items[].configurations.nodes[] | has("children")
then ...
else ...
end

But what is to say that each of the arrays has length equal to 1? Or that the item in the nodes array is a JSON object?

So perhaps it would be better to write something like:

.result.CVE_Items[0].configurations.nodes[0] as $node
| if ($node | ( type == "object" and has("children") ))
  then ...
  else ...
  end

But the same problem exists with the rest of your query, which runs the risk not only of generating an error, but also of generating an unwanted combinatorial explosion.

So what you should probably do is introduce jq variables along the way to ensure that you are only selecting as many items as you really want, as illustrated in the following section.

On a minor note: instead of (. | E) you can simply write E.

Example

    if .totalResults == 1 
    then .result.CVE_Items[] 
    | .cve.CVE_data_meta.ID as $ID
    | .cve.description.description_data[].value as $value
    | .configurations.nodes[] as $node
    | if $node | (type == "object" and has("children"))
      then .impact.baseMetricV3
      |  {CVE: $ID,
          Description: $value, 
          "CVSS V3 Score":  .cvssV3.baseSeverity,
          "Attack Vector":  .cvssV3.attackVector,
          "Privileges Required": .cvssV3.privilegesRequired,
          "User Interaction": .cvssV3.userInteraction,
          "Impact Score": .impactScore,
          "Products Affected": [$node.children[].cpe_match[].cpe23Uri]}
      else {Results: .message} 
      end
    else {Results: .message} 
    end

Upvotes: 0

Related Questions