/ libpkg / pkg_osvf.c
pkg_osvf.c
   1  /*-
   2   * SPDX-License-Identifier: BSD-2-Clause
   3   *
   4   *​ Copyright (c) 2025 The FreeBSD Foundation
   5   *​
   6   *​ Portions of this software were developed by
   7   * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
   8   * the FreeBSD Foundation
   9   */
  10  
  11  
  12  #include <ctype.h>
  13  #include <sys/types.h>
  14  #include <sys/mman.h>
  15  #include <sys/stat.h>
  16  #include <sys/time.h>
  17  #include <stdio.h>
  18  #include <errno.h>
  19  #include <unistd.h>
  20  #include <fcntl.h>
  21  #include <time.h>
  22  #include <xmalloc.h>
  23  
  24  #include "private/pkg_osvf.h"
  25  #include "pkghash.h"
  26  
  27  /*
  28    Open Source Vulnerability format: https://ossf.github.io/osv-schema/
  29    OSVF schema: https://github.com/ossf/osv-schema/blob/main/validation/schema.json
  30    OSVF schema version: 1.7.0
  31   */
  32  static const char osvf_schema_str[] = "{"
  33                                        "  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\","
  34                                        "  \"$id\": \"https://raw.githubusercontent.com/ossf/osv-schema/main/validation/schema.json\","
  35                                        "  \"title\": \"Open Source Vulnerability\","
  36                                        "  \"description\": \"A schema for describing a vulnerability in an open source package. See also https://ossf.github.io/osv-schema/\","
  37                                        "  \"type\": \"object\","
  38                                        "  \"properties\": {"
  39                                        "    \"schema_version\": {"
  40                                        "      \"type\": \"string\""
  41                                        "    },"
  42                                        "    \"id\": {"
  43                                        "      \"$ref\": \"#/$defs/prefix\""
  44                                        "    },"
  45                                        "    \"modified\": {"
  46                                        "      \"$ref\": \"#/$defs/timestamp\""
  47                                        "    },"
  48                                        "    \"published\": {"
  49                                        "      \"$ref\": \"#/$defs/timestamp\""
  50                                        "    },"
  51                                        "    \"withdrawn\": {"
  52                                        "      \"$ref\": \"#/$defs/timestamp\""
  53                                        "    },"
  54                                        "    \"aliases\": {"
  55                                        "      \"type\": ["
  56                                        "        \"array\","
  57                                        "        \"null\""
  58                                        "      ],"
  59                                        "      \"items\": {"
  60                                        "        \"type\": \"string\""
  61                                        "      }"
  62                                        "    },"
  63                                        "    \"related\": {"
  64                                        "      \"type\": \"array\","
  65                                        "      \"items\": {"
  66                                        "        \"type\": \"string\""
  67                                        "      }"
  68                                        "    },"
  69                                        "    \"upstream\": {"
  70                                        "      \"type\": \"array\","
  71                                        "      \"items\": {"
  72                                        "        \"type\": \"string\""
  73                                        "      }"
  74                                        "    },"
  75                                        "    \"summary\": {"
  76                                        "      \"type\": \"string\""
  77                                        "    },"
  78                                        "    \"details\": {"
  79                                        "      \"type\": \"string\""
  80                                        "    },"
  81                                        "    \"severity\": {"
  82                                        "      \"$ref\": \"#/$defs/severity\""
  83                                        "    },"
  84                                        "    \"affected\": {"
  85                                        "      \"type\": ["
  86                                        "        \"array\","
  87                                        "        \"null\""
  88                                        "      ],"
  89                                        "      \"items\": {"
  90                                        "        \"type\": \"object\","
  91                                        "        \"properties\": {"
  92                                        "          \"package\": {"
  93                                        "            \"type\": \"object\","
  94                                        "            \"properties\": {"
  95                                        "              \"ecosystem\": {"
  96                                        "                \"$ref\": \"#/$defs/ecosystemWithSuffix\""
  97                                        "              },"
  98                                        "              \"name\": {"
  99                                        "                \"type\": \"string\""
 100                                        "              },"
 101                                        "              \"purl\": {"
 102                                        "                \"type\": \"string\""
 103                                        "              }"
 104                                        "            },"
 105                                        "            \"required\": ["
 106                                        "              \"ecosystem\","
 107                                        "              \"name\""
 108                                        "            ]"
 109                                        "          },"
 110                                        "          \"severity\": {"
 111                                        "            \"$ref\": \"#/$defs/severity\""
 112                                        "          },"
 113                                        "          \"ranges\": {"
 114                                        "            \"type\": \"array\","
 115                                        "            \"items\": {"
 116                                        "              \"type\": \"object\","
 117                                        "              \"properties\": {"
 118                                        "                \"type\": {"
 119                                        "                  \"type\": \"string\","
 120                                        "                  \"enum\": ["
 121                                        "                    \"GIT\","
 122                                        "                    \"SEMVER\","
 123                                        "                    \"ECOSYSTEM\""
 124                                        "                  ]"
 125                                        "                },"
 126                                        "                \"repo\": {"
 127                                        "                  \"type\": \"string\""
 128                                        "                },"
 129                                        "                \"events\": {"
 130                                        "                  \"title\": \"events must contain an introduced object and may contain fixed, last_affected or limit objects\","
 131                                        "                  \"type\": \"array\","
 132                                        "                  \"contains\": {"
 133                                        "                    \"required\": ["
 134                                        "                      \"introduced\""
 135                                        "                    ]"
 136                                        "                  },"
 137                                        "                  \"items\": {"
 138                                        "                    \"type\": \"object\","
 139                                        "                    \"oneOf\": ["
 140                                        "                      {"
 141                                        "                        \"type\": \"object\","
 142                                        "                        \"properties\": {"
 143                                        "                          \"introduced\": {"
 144                                        "                            \"type\": \"string\""
 145                                        "                          }"
 146                                        "                        },"
 147                                        "                        \"required\": ["
 148                                        "                          \"introduced\""
 149                                        "                        ]"
 150                                        "                      },"
 151                                        "                      {"
 152                                        "                        \"type\": \"object\","
 153                                        "                        \"properties\": {"
 154                                        "                          \"fixed\": {"
 155                                        "                            \"type\": \"string\""
 156                                        "                          }"
 157                                        "                        },"
 158                                        "                        \"required\": ["
 159                                        "                          \"fixed\""
 160                                        "                        ]"
 161                                        "                      },"
 162                                        "                      {"
 163                                        "                        \"type\": \"object\","
 164                                        "                        \"properties\": {"
 165                                        "                          \"last_affected\": {"
 166                                        "                            \"type\": \"string\""
 167                                        "                          }"
 168                                        "                        },"
 169                                        "                        \"required\": ["
 170                                        "                          \"last_affected\""
 171                                        "                        ]"
 172                                        "                      },"
 173                                        "                      {"
 174                                        "                        \"type\": \"object\","
 175                                        "                        \"properties\": {"
 176                                        "                          \"limit\": {"
 177                                        "                            \"type\": \"string\""
 178                                        "                          }"
 179                                        "                        },"
 180                                        "                        \"required\": ["
 181                                        "                          \"limit\""
 182                                        "                        ]"
 183                                        "                      }"
 184                                        "                    ]"
 185                                        "                  },"
 186                                        "                  \"minItems\": 1"
 187                                        "                },"
 188                                        "                \"database_specific\": {"
 189                                        "                  \"type\": \"object\""
 190                                        "                }"
 191                                        "              },"
 192                                        "              \"allOf\": ["
 193                                        "                {"
 194                                        "                  \"title\": \"GIT ranges require a repo\","
 195                                        "                  \"if\": {"
 196                                        "                    \"properties\": {"
 197                                        "                      \"type\": {"
 198                                        "                        \"const\": \"GIT\""
 199                                        "                      }"
 200                                        "                    }"
 201                                        "                  },"
 202                                        "                  \"then\": {"
 203                                        "                    \"required\": ["
 204                                        "                      \"repo\""
 205                                        "                    ]"
 206                                        "                  }"
 207                                        "                },"
 208                                        "                {"
 209                                        "                  \"title\": \"last_affected and fixed events are mutually exclusive\","
 210                                        "                  \"if\": {"
 211                                        "                    \"properties\": {"
 212                                        "                      \"events\": {"
 213                                        "                        \"contains\": {"
 214                                        "                          \"required\": ["
 215                                        "                            \"last_affected\""
 216                                        "                          ]"
 217                                        "                        }"
 218                                        "                      }"
 219                                        "                    }"
 220                                        "                  },"
 221                                        "                  \"then\": {"
 222                                        "                    \"not\": {"
 223                                        "                      \"properties\": {"
 224                                        "                        \"events\": {"
 225                                        "                          \"contains\": {"
 226                                        "                            \"required\": ["
 227                                        "                              \"fixed\""
 228                                        "                            ]"
 229                                        "                          }"
 230                                        "                        }"
 231                                        "                      }"
 232                                        "                    }"
 233                                        "                  }"
 234                                        "                }"
 235                                        "              ],"
 236                                        "              \"required\": ["
 237                                        "                \"type\","
 238                                        "                \"events\""
 239                                        "              ]"
 240                                        "            }"
 241                                        "          },"
 242                                        "          \"versions\": {"
 243                                        "            \"type\": \"array\","
 244                                        "            \"items\": {"
 245                                        "              \"type\": \"string\""
 246                                        "            }"
 247                                        "          },"
 248                                        "          \"ecosystem_specific\": {"
 249                                        "            \"type\": \"object\""
 250                                        "          },"
 251                                        "          \"database_specific\": {"
 252                                        "            \"type\": \"object\""
 253                                        "          }"
 254                                        "        }"
 255                                        "      }"
 256                                        "    },"
 257                                        "    \"references\": {"
 258                                        "      \"type\": ["
 259                                        "        \"array\","
 260                                        "        \"null\""
 261                                        "      ],"
 262                                        "      \"items\": {"
 263                                        "        \"type\": \"object\","
 264                                        "        \"properties\": {"
 265                                        "          \"type\": {"
 266                                        "            \"type\": \"string\","
 267                                        "            \"enum\": ["
 268                                        "              \"ADVISORY\","
 269                                        "              \"ARTICLE\","
 270                                        "              \"DETECTION\","
 271                                        "              \"DISCUSSION\","
 272                                        "              \"REPORT\","
 273                                        "              \"FIX\","
 274                                        "              \"INTRODUCED\","
 275                                        "              \"GIT\","
 276                                        "              \"PACKAGE\","
 277                                        "              \"EVIDENCE\","
 278                                        "              \"WEB\""
 279                                        "            ]"
 280                                        "          },"
 281                                        "          \"url\": {"
 282                                        "            \"type\": \"string\","
 283                                        "            \"format\": \"uri\""
 284                                        "          }"
 285                                        "        },"
 286                                        "        \"required\": ["
 287                                        "          \"type\","
 288                                        "          \"url\""
 289                                        "        ]"
 290                                        "      }"
 291                                        "    },"
 292                                        "    \"credits\": {"
 293                                        "      \"type\": \"array\","
 294                                        "      \"items\": {"
 295                                        "        \"type\": \"object\","
 296                                        "        \"properties\": {"
 297                                        "          \"name\": {"
 298                                        "            \"type\": \"string\""
 299                                        "          },"
 300                                        "          \"contact\": {"
 301                                        "            \"type\": \"array\","
 302                                        "            \"items\": {"
 303                                        "              \"type\": \"string\""
 304                                        "            }"
 305                                        "          },"
 306                                        "          \"type\": {"
 307                                        "            \"type\": \"string\","
 308                                        "            \"enum\": ["
 309                                        "              \"FINDER\","
 310                                        "              \"REPORTER\","
 311                                        "              \"ANALYST\","
 312                                        "              \"COORDINATOR\","
 313                                        "              \"REMEDIATION_DEVELOPER\","
 314                                        "              \"REMEDIATION_REVIEWER\","
 315                                        "              \"REMEDIATION_VERIFIER\","
 316                                        "              \"TOOL\","
 317                                        "              \"SPONSOR\","
 318                                        "              \"OTHER\""
 319                                        "            ]"
 320                                        "          }"
 321                                        "        },"
 322                                        "        \"required\": ["
 323                                        "          \"name\""
 324                                        "        ]"
 325                                        "      }"
 326                                        "    },"
 327                                        "    \"database_specific\": {"
 328                                        "      \"type\": \"object\""
 329                                        "    }"
 330                                        "  },"
 331                                        "  \"required\": ["
 332                                        "    \"id\","
 333                                        "    \"modified\""
 334                                        "  ],"
 335                                        "  \"allOf\": ["
 336                                        "    {"
 337                                        "      \"if\": {"
 338                                        "        \"required\": ["
 339                                        "          \"severity\""
 340                                        "        ]"
 341                                        "      },"
 342                                        "      \"then\": {"
 343                                        "        \"properties\": {"
 344                                        "          \"affected\": {"
 345                                        "            \"items\": {"
 346                                        "              \"properties\": {"
 347                                        "                \"severity\": {"
 348                                        "                  \"type\": \"null\""
 349                                        "                }"
 350                                        "              }"
 351                                        "            }"
 352                                        "          }"
 353                                        "        }"
 354                                        "      }"
 355                                        "    }"
 356                                        "  ],"
 357                                        "  \"$defs\": {"
 358                                        "    \"ecosystemName\": {"
 359                                        "      \"type\": \"string\","
 360                                        "      \"title\": \"Currently supported ecosystems\","
 361                                        "      \"description\": \"These ecosystems are also documented at https://ossf.github.io/osv-schema/#affectedpackage-field\","
 362                                        "      \"enum\": ["
 363                                        "        \"AlmaLinux\","
 364                                        "        \"Alpine\","
 365                                        "        \"Android\","
 366                                        "        \"Bioconductor\","
 367                                        "        \"Bitnami\","
 368                                        "        \"Chainguard\","
 369                                        "        \"ConanCenter\","
 370                                        "        \"CRAN\","
 371                                        "        \"crates.io\","
 372                                        "        \"Debian\","
 373                                        "        \"FreeBSD\","
 374                                        "        \"GHC\","
 375                                        "        \"GitHub Actions\","
 376                                        "        \"Go\","
 377                                        "        \"Hackage\","
 378                                        "        \"Hex\","
 379                                        "        \"Kubernetes\","
 380                                        "        \"Linux\","
 381                                        "        \"Mageia\","
 382                                        "        \"Maven\","
 383                                        "        \"MinimOS\","
 384                                        "        \"npm\","
 385                                        "        \"NuGet\","
 386                                        "        \"openSUSE\","
 387                                        "        \"OSS-Fuzz\","
 388                                        "        \"Packagist\","
 389                                        "        \"Photon OS\","
 390                                        "        \"Pub\","
 391                                        "        \"PyPI\","
 392                                        "        \"Red Hat\","
 393                                        "        \"Rocky Linux\","
 394                                        "        \"RubyGems\","
 395                                        "        \"SUSE\","
 396                                        "        \"SwiftURL\","
 397                                        "        \"Ubuntu\","
 398                                        "        \"Wolfi\""
 399                                        "      ]"
 400                                        "    },"
 401                                        "    \"ecosystemSuffix\": {"
 402                                        "      \"type\": \"string\","
 403                                        "      \"pattern\": \":.+\""
 404                                        "    },"
 405                                        "    \"ecosystemWithSuffix\": {"
 406                                        "      \"type\": \"string\","
 407                                        "      \"title\": \"Currently supported ecosystems\","
 408                                        "      \"description\": \"These ecosystems are also documented at https://ossf.github.io/osv-schema/#affectedpackage-field\","
 409                                        "      \"pattern\": \"^(AlmaLinux|Alpine|Android|Bioconductor|Bitnami|Chainguard|ConanCenter|CRAN|crates\\.io|Debian|FreeBSD:ports|FreeBSD|GHC|GitHub Actions|Go|Hackage|Hex|Kubernetes|Linux|Mageia|Maven|MinimOS|npm|NuGet|openSUSE|OSS-Fuzz|Packagist|Photon OS|Pub|PyPI|Red Hat|Rocky Linux|RubyGems|SUSE|SwiftURL|Ubuntu|Wolfi|GIT)(:.+)?$\""
 410                                        "    },"
 411                                        "    \"prefix\": {"
 412                                        "      \"type\": \"string\","
 413                                        "      \"title\": \"Currently supported home database identifier prefixes\","
 414                                        "      \"description\": \"These home databases are also documented at https://ossf.github.io/osv-schema/#id-modified-fields\","
 415                                        "      \"pattern\": \"^(ASB-A|PUB-A|ALSA|ALBA|ALEA|BIT|CGA|CURL|CVE|DSA|DLA|ELA|FBSD|DTSA|GHSA|GO|GSD|HSEC|KUBE|LBSEC|LSN|MAL|MGASA|OSV|openSUSE-SU|PHSA|PSF|PYSEC|RHBA|RHEA|RHSA|RLSA|RXSA|RSEC|RUSTSEC|SUSE-[SRFO]U|UBUNTU|USN|V8)-\""
 416                                        "    },"
 417                                        "    \"severity\": {"
 418                                        "      \"type\": ["
 419                                        "        \"array\","
 420                                        "        \"null\""
 421                                        "      ],"
 422                                        "      \"items\": {"
 423                                        "        \"type\": \"object\","
 424                                        "        \"properties\": {"
 425                                        "          \"type\": {"
 426                                        "            \"type\": \"string\","
 427                                        "            \"enum\": ["
 428                                        "              \"CVSS_V2\","
 429                                        "              \"CVSS_V3\","
 430                                        "              \"CVSS_V4\","
 431                                        "              \"Ubuntu\""
 432                                        "            ]"
 433                                        "          },"
 434                                        "          \"score\": {"
 435                                        "            \"type\": \"string\""
 436                                        "          }"
 437                                        "        },"
 438                                        "        \"allOf\": ["
 439                                        "          {"
 440                                        "            \"if\": {"
 441                                        "              \"properties\": {"
 442                                        "                \"type\": {"
 443                                        "                  \"const\": \"CVSS_V2\""
 444                                        "                }"
 445                                        "              }"
 446                                        "            },"
 447                                        "            \"then\": {"
 448                                        "              \"properties\": {"
 449                                        "                \"score\": {"
 450                                        "                  \"pattern\": \"^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$\""
 451                                        "                }"
 452                                        "              }"
 453                                        "            }"
 454                                        "          },"
 455                                        "          {"
 456                                        "            \"if\": {"
 457                                        "              \"properties\": {"
 458                                        "                \"type\": {"
 459                                        "                  \"const\": \"CVSS_V3\""
 460                                        "                }"
 461                                        "              }"
 462                                        "            },"
 463                                        "            \"then\": {"
 464                                        "              \"properties\": {"
 465                                        "                \"score\": {"
 466                                        "                  \"pattern\": \"^CVSS:3[.][01]/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$\""
 467                                        "                }"
 468                                        "              }"
 469                                        "            }"
 470                                        "          },"
 471                                        "          {"
 472                                        "            \"if\": {"
 473                                        "              \"properties\": {"
 474                                        "                \"type\": {"
 475                                        "                  \"const\": \"CVSS_V4\""
 476                                        "                }"
 477                                        "              }"
 478                                        "            },"
 479                                        "            \"then\": {"
 480                                        "              \"properties\": {"
 481                                        "                \"score\": {"
 482                                        "                  \"pattern\": \"^CVSS:4[.]0/AV:[NALP]/AC:[LH]/AT:[NP]/PR:[NLH]/UI:[NPA]/VC:[HLN]/VI:[HLN]/VA:[HLN]/SC:[HLN]/SI:[HLN]/SA:[HLN](/E:[XAPU])?(/CR:[XHML])?(/IR:[XHML])?(/AR:[XHML])?(/MAV:[XNALP])?(/MAC:[XLH])?(/MAT:[XNP])?(/MPR:[XNLH])?(/MUI:[XNPA])?(/MVC:[XNLH])?(/MVI:[XNLH])?(/MVA:[XNLH])?(/MSC:[XNLH])?(/MSI:[XNLHS])?(/MSA:[XNLHS])?(/S:[XNP])?(/AU:[XNY])?(/R:[XAUI])?(/V:[XDC])?(/RE:[XLMH])?(/U:(X|Clear|Green|Amber|Red))?$\""
 483                                        "                }"
 484                                        "              }"
 485                                        "            }"
 486                                        "          },"
 487                                        "          {"
 488                                        "            \"if\": {"
 489                                        "              \"properties\": {"
 490                                        "                \"type\": {"
 491                                        "                  \"const\": \"Ubuntu\""
 492                                        "                }"
 493                                        "              }"
 494                                        "            },"
 495                                        "            \"then\": {"
 496                                        "              \"properties\": {"
 497                                        "                \"score\": {"
 498                                        "                  \"enum\": ["
 499                                        "                    \"negligible\","
 500                                        "                    \"low\","
 501                                        "                    \"medium\","
 502                                        "                    \"high\","
 503                                        "                    \"critical\""
 504                                        "                  ]"
 505                                        "                }"
 506                                        "              }"
 507                                        "            }"
 508                                        "          }"
 509                                        "        ],"
 510                                        "        \"required\": ["
 511                                        "          \"type\","
 512                                        "          \"score\""
 513                                        "        ]"
 514                                        "      }"
 515                                        "    },"
 516                                        "    \"timestamp\": {"
 517                                        "      \"type\": \"string\","
 518                                        "      \"format\": \"date-time\","
 519                                        "      \"pattern\": \"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?Z\""
 520                                        "    }"
 521                                        "  },"
 522                                        "  \"additionalProperties\": false"
 523                                        "}";
 524  
 525  struct pkg_osvf_hash
 526  {
 527  	unsigned int value;
 528  	char *name;
 529  };
 530  
 531  static struct pkg_osvf_hash references_global[] =
 532  {
 533  	{OSVF_REFERENCE_ADVISORY, "ADVISORY"},
 534  	{OSVF_REFERENCE_ARTICLE, "ARTICLE"},
 535  	{OSVF_REFERENCE_DETECTION, "DETECTION"},
 536  	{OSVF_REFERENCE_DISCUSSION, "DISCUSSION"},
 537  	{OSVF_REFERENCE_REPORT, "REPORT"},
 538  	{OSVF_REFERENCE_FIX, "FIX"},
 539  	{OSVF_REFERENCE_INTRODUCED, "INTRODUCED"},
 540  	{OSVF_REFERENCE_PACKAGE, "PACKAGE"},
 541  	{OSVF_REFERENCE_EVIDENCE, "EVIDENCE"},
 542  	{OSVF_REFERENCE_WEB, "WEB"},
 543  	{OSVF_REFERENCE_UNKNOWN, NULL}
 544  };
 545  
 546  static struct pkg_osvf_hash event_global[] =
 547  {
 548  	{OSVF_EVENT_VERSION_SEMVER, "SEMVER"},
 549  	{OSVF_EVENT_VERSION_ECOSYSTEM, "ECOSYSTEM"},
 550  	{OSVF_EVENT_VERSION_GIT, "GIT"},
 551  	{OSVF_EVENT_VERSION_UNKNOWN, NULL}
 552  };
 553  
 554  static ucl_object_t *
 555  create_schema_obj(void)
 556  {
 557  	struct ucl_parser *uclparser;
 558  	ucl_object_t *obj = NULL;
 559  
 560  	uclparser = ucl_parser_new (0);
 561  	ucl_parser_add_string(uclparser, osvf_schema_str, 0);
 562  	if (ucl_parser_get_error(uclparser) != NULL)
 563  	{
 564  		pkg_emit_error("Error occurred: %s\n", ucl_parser_get_error (uclparser));
 565  		ucl_parser_free (uclparser);
 566  		return (NULL);
 567  	}
 568  
 569  	obj = ucl_parser_get_object(uclparser);
 570  	ucl_parser_free(uclparser);
 571  	return obj;
 572  }
 573  
 574  
 575  ucl_object_t *
 576  pkg_osvf_open(const char *location)
 577  {
 578  	struct ucl_parser *uclparser;
 579  	ucl_object_t *obj = NULL;
 580  	int fd;
 581  	ucl_object_t *schema = NULL;
 582  	struct ucl_schema_error err;
 583  
 584  	fd = open(location, O_RDONLY);
 585  	if (fd == -1)
 586  	{
 587  		pkg_emit_error("Unable to open OSVF file: %s", location);
 588  		return (NULL);
 589  	}
 590  
 591  	uclparser = ucl_parser_new(0);
 592  	if (!ucl_parser_add_fd(uclparser, fd))
 593  	{
 594  		pkg_emit_error("Error parsing UCL file '%s': %s'",
 595  		               location, ucl_parser_get_error(uclparser));
 596  		ucl_parser_free(uclparser);
 597  		close(fd);
 598  		return (NULL);
 599  	}
 600  	close(fd);
 601  
 602  	obj = ucl_parser_get_object(uclparser);
 603  	ucl_parser_free(uclparser);
 604  
 605  	if (obj == NULL)
 606  	{
 607  		pkg_emit_error("UCL definition %s cannot be validated: %s",
 608  		               location, err.msg);
 609  		return (NULL);
 610  	}
 611  
 612  	schema = create_schema_obj();
 613  
 614  	if (schema == NULL)
 615  	{
 616  		return (NULL);
 617  	}
 618  
 619  	if (!ucl_object_validate(schema, obj, &err))
 620  	{
 621  		pkg_emit_error("UCL definition %s cannot be validated: %s",
 622  		               location, err.msg);
 623  		ucl_object_unref(schema);
 624  		ucl_object_unref(obj);
 625  		return (NULL);
 626  	}
 627  
 628  	ucl_object_unref(schema);
 629  
 630  	return (obj);
 631  }
 632  
 633  static struct pkg_audit_entry *
 634  pkg_osvf_new_entry(void)
 635  {
 636  	struct pkg_audit_entry *entry = xcalloc(1, sizeof(struct pkg_audit_entry));
 637  
 638  	entry->packages = xcalloc(1, sizeof(struct pkg_audit_package));
 639  	entry->names = xcalloc(1, sizeof(struct pkg_audit_pkgname));
 640  	entry->versions = xcalloc(1, sizeof(struct pkg_audit_versions_range));
 641  	entry->cve = xcalloc(1, sizeof(struct pkg_audit_cve));
 642  	entry->references = xcalloc(1, sizeof(struct pkg_audit_reference));
 643  
 644  	return entry;
 645  }
 646  
 647  static void
 648  pkg_osvf_free_pkgname(struct pkg_audit_pkgname *pkgname)
 649  {
 650  	if(!pkgname)
 651  	{
 652  		return;
 653  	}
 654  
 655  	free(pkgname->pkgname);
 656  	pkgname->pkgname = NULL;
 657  
 658  	pkg_osvf_free_pkgname(pkgname->next);
 659  	pkgname->next = NULL;
 660  
 661  	free(pkgname);
 662  }
 663  
 664  static void
 665  pkg_osvf_free_version(struct pkg_audit_version *ver)
 666  {
 667  	if(!ver)
 668  	{
 669  		return;
 670  	}
 671  
 672  	free(ver->version);
 673  	ver->version = NULL;
 674  
 675  	free(ver);
 676  }
 677  
 678  static void
 679  pkg_osvf_free_range(struct pkg_audit_versions_range *range)
 680  {
 681  	free(range);
 682  }
 683  
 684  void
 685  pkg_osvf_free_ecosystem(struct pkg_audit_ecosystem *ecosystem)
 686  {
 687  	if(!ecosystem)
 688  	{
 689  		return;
 690  	}
 691  
 692  	free(ecosystem->original);
 693  	ecosystem->original = NULL;
 694  
 695  	free(ecosystem->name);
 696  	ecosystem->name = NULL;
 697  
 698  
 699  	ucl_object_unref(ecosystem->params);
 700  	ecosystem->params = NULL;
 701  
 702  	free(ecosystem);
 703  }
 704  
 705  static void
 706  pkg_osvf_free_package(struct pkg_audit_package *package)
 707  {
 708  	if(!package)
 709  	{
 710  		return;
 711  	}
 712  
 713  	pkg_osvf_free_pkgname(package->names);
 714  	package->names = NULL;
 715  
 716  	pkg_osvf_free_range(package->versions);
 717  	package->versions = NULL;
 718  
 719  	pkg_osvf_free_ecosystem(package->ecosystem);
 720  	package->ecosystem = NULL;
 721  
 722  	pkg_osvf_free_package(package->next);
 723  	package->next = NULL;
 724  
 725  	free(package);
 726  }
 727  
 728  static void
 729  pkg_osvf_free_cve(struct pkg_audit_cve *cve)
 730  {
 731  	if(!cve)
 732  	{
 733  		return;
 734  	}
 735  
 736  	free(cve->cvename);
 737  	cve->cvename = NULL;
 738  
 739  	pkg_osvf_free_cve(cve->next);
 740  	cve->next = NULL;
 741  
 742  	free(cve);
 743  }
 744  
 745  static void
 746  pkg_osvf_free_reference(struct pkg_audit_reference *reference)
 747  {
 748  	if(!reference)
 749  	{
 750  		return;
 751  	}
 752  
 753  	free(reference->url);
 754  	reference->url = NULL;
 755  
 756  	pkg_osvf_free_reference(reference->next);
 757  	reference->next = NULL;
 758  
 759  	free(reference);
 760  }
 761  
 762  void
 763  pkg_osvf_free_entry(struct pkg_audit_entry *entry)
 764  {
 765  	struct pkg_audit_versions_range *versions = NULL;
 766  	struct pkg_audit_versions_range *next_versions = NULL;
 767  
 768  	struct pkg_audit_pkgname *names = NULL;
 769  	struct pkg_audit_pkgname *next_names = NULL;
 770  
 771  	if(!entry)
 772  	{
 773  		return;
 774  	}
 775  
 776  	versions = entry->versions;
 777  	names = entry->names;
 778  
 779  	if(entry->id)
 780  	{
 781  		free(entry->id);
 782  		entry->id = NULL;
 783  	}
 784  	if(entry->desc)
 785  	{
 786  		free(entry->desc);
 787  		entry->desc = NULL;
 788  	}
 789  
 790  	while(versions)
 791  	{
 792  		next_versions = versions->next;
 793  		free(versions);
 794  		versions = next_versions;
 795  	}
 796  
 797  	while(names)
 798  	{
 799  		next_names = names->next;
 800  		free(names);
 801  		names = next_names;
 802  	}
 803  
 804  	pkg_osvf_free_package(entry->packages);
 805  	entry->packages = NULL;
 806  
 807  	pkg_osvf_free_cve(entry->cve);
 808  	entry->cve = NULL;
 809  
 810  	pkg_osvf_free_reference(entry->references);
 811  	entry->references = NULL;
 812  
 813  	free(entry);
 814  }
 815  
 816  static struct pkghash *
 817  pkg_osvf_create_seek_hash(struct pkg_osvf_hash *osvf_ptr)
 818  {
 819  	struct pkghash *hash_table = pkghash_new();
 820  
 821  	while(osvf_ptr->name)
 822  	{
 823  		pkghash_add(hash_table, osvf_ptr->name, osvf_ptr, NULL);
 824  		osvf_ptr ++;
 825  	}
 826  
 827  	return hash_table;
 828  }
 829  
 830  static unsigned int
 831  pkg_osvf_get_hash(const char *key, struct pkg_osvf_hash *global, unsigned int unknow)
 832  {
 833  	struct pkghash *hash = NULL;
 834  	pkghash_entry *entry = NULL;
 835  	struct pkg_osvf_hash *rtn_struct = NULL;
 836  	unsigned int rtn_value = unknow;
 837  
 838  	if(!key)
 839  	{
 840  		return rtn_value;
 841  	}
 842  
 843  	/*
 844  	 * Create seek table with struct
 845  	 * Make easier to seek
 846  	 */
 847  	hash = pkg_osvf_create_seek_hash(global);
 848  
 849  	entry = pkghash_get(hash, key);
 850  
 851  	/*
 852  	 * If there was key then get it and
 853  	 * free hash table as we don't need it anymore
 854  	 */
 855  	if(entry)
 856  	{
 857  		rtn_struct = (struct pkg_osvf_hash *) entry->value;
 858  		rtn_value = rtn_struct->value;
 859  		pkghash_destroy(hash);
 860  	}
 861  
 862  	return rtn_value;
 863  }
 864  
 865  
 866  struct pkg_audit_ecosystem *
 867  pkg_osvf_get_ecosystem(const char *ecosystem)
 868  {
 869  	char ecosystem_delimiter[] = ":";
 870  	char *ecosystem_copy = NULL;
 871  	char *ecosystem_token = NULL;
 872  	struct pkg_audit_ecosystem *rtn_ecosystem = NULL;
 873  
 874  	if(!ecosystem)
 875  	{
 876  		return NULL;
 877  	}
 878  
 879  	ecosystem_copy = xstrdup(ecosystem);
 880  	ecosystem_token = strtok(ecosystem_copy, ecosystem_delimiter);
 881  
 882  	if(!ecosystem_token)
 883  	{
 884  		free(ecosystem_copy);
 885  		return NULL;
 886  	}
 887  
 888  	rtn_ecosystem = xcalloc(1, sizeof(struct pkg_audit_ecosystem));
 889  	rtn_ecosystem->original = xstrdup(ecosystem);
 890  	rtn_ecosystem->name = xstrdup(ecosystem_token);
 891  	rtn_ecosystem->params = ucl_object_typed_new(UCL_ARRAY);
 892  
 893  	/*
 894  	 * Parse other information out of Ecosystem tags like
 895  	 * Alpine:v3.16
 896  	 * FreeBSD:ports
 897  	 * FreeBSD:kernel:14.3
 898  	 * FreeBSD:src:14.3
 899  	 * Mageia:9
 900  	 * Maven:https://repo1.maven.org/maven2/
 901  	 * Photon OS:3.0
 902  	 * Red Hat:rhel_aus:8.4::appstream
 903  	 * Ubuntu:22.04:LTS
 904  	 * Ubuntu:Pro:18.04:LTS
 905  	 * to array for more processing further
 906  	 */
 907  	while(ecosystem_token)
 908  	{
 909  		ecosystem_token = strtok(NULL, ecosystem_delimiter);
 910  		if(ecosystem_token)
 911  		{
 912  			ucl_array_append(rtn_ecosystem->params, ucl_object_fromstring(ecosystem_token));
 913  		}
 914  	}
 915  
 916  	free(ecosystem_copy);
 917  	ecosystem_copy = NULL;
 918  
 919  	return rtn_ecosystem;
 920  }
 921  
 922  unsigned int
 923  pkg_osvf_get_reference(const char *reference_type)
 924  {
 925  	return pkg_osvf_get_hash(reference_type, references_global, OSVF_REFERENCE_UNKNOWN);
 926  }
 927  
 928  unsigned int
 929  pkg_osvf_get_event(const char *event_type)
 930  {
 931  	return pkg_osvf_get_hash(event_type, event_global, OSVF_EVENT_VERSION_UNKNOWN);
 932  }
 933  
 934  
 935  static const char *
 936  pkg_osvf_ucl_string(const ucl_object_t *obj, const char *key)
 937  {
 938  	const ucl_object_t *key_obj = ucl_object_find_key(obj, key);
 939  
 940  	if(key_obj && ucl_object_type(key_obj) == UCL_STRING)
 941  	{
 942  		return ucl_object_tostring(key_obj);
 943  	}
 944  
 945  	return "";
 946  }
 947  
 948  static void
 949  pkg_osvf_parse_package(struct pkg_audit_package *package, const ucl_object_t *package_obj)
 950  {
 951  	/* Parses package structure:
 952  	   "package": {
 953  	     "ecosystem": "FreeBSD:ports",
 954  	     "name": "packagename"
 955  	    },
 956  	*/
 957  
 958  	if(!package_obj || ucl_object_type(package_obj) != UCL_OBJECT)
 959  	{
 960  		return;
 961  	}
 962  
 963  	package->names->pkgname = xstrdup(pkg_osvf_ucl_string(package_obj, "name"));
 964  	package->ecosystem = pkg_osvf_get_ecosystem(pkg_osvf_ucl_string(package_obj, "ecosystem"));
 965  }
 966  
 967  static void
 968  pkg_osvf_parse_events(struct pkg_audit_versions_range *range, const ucl_object_t *event_array, const char *type)
 969  {
 970  	ucl_object_iter_t it = NULL;
 971  	const ucl_object_t *cur = NULL;
 972  
 973  	if(!event_array || ucl_object_type(event_array) != UCL_ARRAY)
 974  	{
 975  		return;
 976  	}
 977  
 978  	if(!type)
 979  	{
 980  		return;
 981  	}
 982  
 983  	range->type = pkg_osvf_get_event(type);
 984  
 985  	/* Parses package structure from events:
 986  	   {
 987  	     "fixed|introduced": "1.0.0"
 988  	   }
 989  	*/
 990  
 991  	while ((cur = ucl_iterate_object(event_array, &it, true)))
 992  	{
 993  		if(ucl_object_find_key(cur, "fixed"))
 994  		{
 995  			range->v2.version = xstrdup(pkg_osvf_ucl_string(cur, "fixed"));
 996  			printf("Fixed: %s\n", range->v2.version);
 997  			range->v2.type = OSVF_EVENT_FIXED;
 998  		}
 999  		else if(ucl_object_find_key(cur, "introduced"))
1000  		{
1001  			range->v1.version = xstrdup(pkg_osvf_ucl_string(cur, "introduced"));
1002  			printf("Intro: %s\n", range->v1.version);
1003  			range->v1.type = OSVF_EVENT_INTRODUCED;
1004  		}
1005  	}
1006  }
1007  
1008  
1009  static void
1010  pkg_osvf_parse_ranges(struct pkg_audit_versions_range *range, const ucl_object_t *range_array)
1011  {
1012  	ucl_object_iter_t it = NULL;
1013  	const ucl_object_t *cur = NULL;
1014  	struct pkg_audit_versions_range *next_range = NULL;
1015  	const ucl_object_t *sub_obj = NULL;
1016  	bool is_first = true;
1017  
1018  	if(!range_array || ucl_object_type(range_array) != UCL_ARRAY)
1019  	{
1020  		return;
1021  	}
1022  
1023  	/* Parses events structure
1024  	   [
1025  	   "type": "SEMVER",
1026  	   "events": [
1027  	     {
1028  	       "fixed": "1.0.0"
1029  	     },
1030  	     {
1031  	     "introduced": "0.0.1"
1032  	     },
1033  	   ]
1034  	*/
1035  
1036  	while ((cur = ucl_iterate_object(range_array, &it, true)))
1037  	{
1038  		if(is_first == false)
1039  		{
1040  			next_range = xcalloc(1, sizeof(struct pkg_audit_versions_range));
1041  			range->next = next_range;
1042  			range = next_range;
1043  		}
1044  
1045  		sub_obj = ucl_object_find_key(cur, "events");
1046  
1047  		if(sub_obj && ucl_object_type(sub_obj) == UCL_ARRAY)
1048  		{
1049  			pkg_osvf_parse_events(range, ucl_object_find_key(cur, "events"), pkg_osvf_ucl_string(cur, "type"));
1050  		}
1051  
1052  		is_first = false;
1053  	}
1054  }
1055  
1056  static void
1057  pkg_osvf_parse_reference(struct pkg_audit_reference *ref, const ucl_object_t *ref_obj)
1058  {
1059  	if(!ref_obj || ucl_object_type(ref_obj) != UCL_OBJECT)
1060  	{
1061  		return;
1062  	}
1063  
1064  	/*
1065  	   Parses refrence to struct
1066  	   {
1067  	     "type": "ADVISORY",
1068  	     "url": "https://www.freebsd.org/"
1069  	   }
1070  	*/
1071  	ref->url = xstrdup(pkg_osvf_ucl_string(ref_obj, "url"));
1072  	ref->type = pkg_osvf_get_reference(pkg_osvf_ucl_string(ref_obj, "type"));
1073  }
1074  
1075  static void
1076  pkg_osvf_parse_references(struct pkg_audit_entry *entry, const ucl_object_t *ref_obj)
1077  {
1078  	ucl_object_iter_t it = NULL;
1079  	const ucl_object_t *cur = NULL;
1080  	bool is_first = true;
1081  	struct pkg_audit_reference *reference = entry->references;
1082  	struct pkg_audit_reference *next_package = NULL;
1083  
1084  
1085  	if(!ref_obj || ucl_object_type(ref_obj) != UCL_ARRAY)
1086  	{
1087  		return;
1088  	}
1089  
1090  	/*
1091  	   Parses refereces array to linked list
1092  	   "references": [
1093  	     {
1094  	       "type": "ADVISORY",
1095  	       "url": "https://www.freebsd.org/"
1096  	     }
1097  	    ]
1098  	*/
1099  
1100  	while ((cur = ucl_iterate_object(ref_obj, &it, true)))
1101  	{
1102  		if(is_first == false)
1103  		{
1104  			next_package = xcalloc(1, sizeof(struct pkg_audit_reference));
1105  			reference->next = next_package;
1106  			reference = next_package;
1107  		}
1108  
1109  		if(ucl_object_type(cur) == UCL_OBJECT)
1110  		{
1111  			pkg_osvf_parse_reference(reference, cur);
1112  		}
1113  
1114  		is_first = false;
1115  	}
1116  
1117  }
1118  
1119  static void
1120  pkg_osvf_parse_affected(struct pkg_audit_entry *entry, const ucl_object_t *aff_obj)
1121  {
1122  	ucl_object_iter_t it = NULL;
1123  	const ucl_object_t *cur = NULL;
1124  	const ucl_object_t *array_obj = NULL;
1125  	bool is_first = true;
1126  	struct pkg_audit_package *package = entry->packages;
1127  	struct pkg_audit_package *next_package = NULL;
1128  
1129  	if(!aff_obj || ucl_object_type(aff_obj) != UCL_ARRAY)
1130  	{
1131  		return;
1132  	}
1133  
1134  	/* Parse affected to correct structs
1135  	   "affected": [
1136  	     {
1137  	       "package": {
1138  	       "ecosystem": "FreeBSD:ports",
1139  	       "name": "osvf-test-package10"
1140  	     },
1141  	   "ranges": [
1142  	     {
1143  	       "type": "SEMVER",
1144  	       "events": [
1145  	        {
1146  	          "fixed": "1.0.0"
1147  	        },
1148  	        {
1149  	          "introduced": "0.0.1"
1150  	        },
1151  	      ]
1152  	     }
1153  	     ]
1154  	    }
1155  	   ]
1156  	*/
1157  	while ((cur = ucl_iterate_object(aff_obj, &it, true)))
1158  	{
1159  		if(is_first == false)
1160  		{
1161  			next_package = xcalloc(1, sizeof(struct pkg_audit_package));
1162  			package->next = next_package;
1163  			package = next_package;
1164  		}
1165  
1166  		array_obj = ucl_object_find_key(cur, "package");
1167  
1168  		if(array_obj && ucl_object_type(aff_obj) == UCL_ARRAY)
1169  		{
1170  			package->names = xcalloc(1, sizeof(struct pkg_audit_pkgname));
1171  			pkg_osvf_parse_package(package, array_obj);
1172  		}
1173  
1174  		array_obj = ucl_object_find_key(cur, "ranges");
1175  
1176  		if(array_obj && ucl_object_type(array_obj) == UCL_ARRAY)
1177  		{
1178  			package->versions = xcalloc(1, sizeof(struct pkg_audit_versions_range));
1179  			pkg_osvf_parse_ranges(package->versions, array_obj);
1180  		}
1181  
1182  		is_first = false;
1183  	}
1184  }
1185  
1186  static void
1187  pkg_osvf_append_version_range(struct pkg_audit_versions_range *to, struct pkg_audit_versions_range *from)
1188  {
1189  	struct pkg_audit_versions_range *ptr_from = from;
1190  	struct pkg_audit_versions_range *ptr_to = to;
1191  
1192  	to->v1.type = from->v1.type;
1193  	to->v1.version = from->v1.version;
1194  	to->v2.type = from->v2.type;
1195  	to->v2.version = from->v2.version;
1196  	to->type = from->type;
1197  
1198  	while(ptr_from->next)
1199  	{
1200  		ptr_to->next = xcalloc(1, sizeof(struct pkg_audit_versions_range));
1201  
1202  		ptr_to = ptr_to->next;
1203  		ptr_from = ptr_from->next;
1204  
1205  		ptr_to->v1.type = ptr_from->v1.type;
1206  		ptr_to->v1.version = ptr_from->v1.version;
1207  		ptr_to->v2.type = ptr_from->v2.type;
1208  		ptr_to->v2.version = ptr_from->v2.version;
1209  		ptr_to->type = ptr_from->type;
1210  	}
1211  }
1212  
1213  static void
1214  pkg_osvf_print_version_type(struct pkg_audit_versions_range *versions)
1215  {
1216  	if(!versions)
1217  	{
1218  		return;
1219  	}
1220  
1221  	printf("\t\tVersion type: ");
1222  	switch(versions->type)
1223  	{
1224  	case OSVF_EVENT_VERSION_UNKNOWN:
1225  		printf("UNKNOWN\n");
1226  		break;
1227  	case OSVF_EVENT_VERSION_SEMVER:
1228  		printf("Sematic Version 2.0\n");
1229  		break;
1230  	case OSVF_EVENT_VERSION_ECOSYSTEM:
1231  		printf("Ecosystem\n");
1232  		break;
1233  	case OSVF_EVENT_VERSION_GIT:
1234  		printf("Git hash\n");
1235  		break;
1236  	}
1237  }
1238  
1239  static void
1240  pkg_osvf_print_version(struct pkg_audit_version *version)
1241  {
1242  	if(!version)
1243  	{
1244  		return;
1245  	}
1246  
1247  	switch(version->type)
1248  	{
1249  	case OSVF_EVENT_UNKNOWN:
1250  		printf("\t\tUnknown type: ");
1251  		break;
1252  	case OSVF_EVENT_INTRODUCED:
1253  		printf("\t\tIntroduced: ");
1254  		break;
1255  	case OSVF_EVENT_FIXED:
1256  		printf("\t\tFixed: ");
1257  		break;
1258  	case OSVF_EVENT_LAST_AFFECTED:
1259  		printf("\t\tAffected: ");
1260  		break;
1261  	case OSVF_EVENT_LIMIT:
1262  		printf("\t\tLimit: ");
1263  		break;
1264  	}
1265  
1266  	printf("%s\n", version->version);
1267  }
1268  
1269  static void
1270  pkg_osvf_print_ecosystem(struct pkg_audit_ecosystem *ecosystem)
1271  {
1272  	ucl_object_iter_t it = NULL;
1273  	const ucl_object_t *cur = NULL;
1274  	int loc = 0;
1275  
1276  	if(!ecosystem)
1277  	{
1278  		return;
1279  	}
1280  
1281  	printf("\t\tEcosystem: ");
1282  
1283  	if(ecosystem->name)
1284  	{
1285  		printf("%s (", ecosystem->name);
1286  	}
1287  
1288  	while ((cur = ucl_iterate_object(ecosystem->params, &it, true)))
1289  	{
1290  		if(loc)
1291  		{
1292  			printf(",");
1293  		}
1294  
1295  		if(ucl_object_type(cur) == UCL_STRING)
1296  		{
1297  			printf("%s", ucl_object_tostring(cur));
1298  		}
1299  
1300  		loc ++;
1301  	}
1302  
1303  	printf(")\n");
1304  }
1305  
1306  void
1307  pkg_osvf_print_entry(struct pkg_audit_entry *entry)
1308  {
1309  	char buf[255];
1310  	struct pkg_audit_package *packages = NULL;
1311  	struct pkg_audit_versions_range *versions = NULL;
1312  	struct pkg_audit_reference *references;
1313  	struct pkg_audit_pkgname *names = NULL;
1314  	bool is_first = true;
1315  
1316  	if(!entry)
1317  	{
1318  		return;
1319  	}
1320  
1321  	names = entry->names;
1322  
1323  	printf("OSVF Vulnerability information:\n");
1324  	printf("\tPackage name: %s\n", entry->pkgname);
1325  	printf("\tPackage names: ");
1326  	while(names)
1327  	{
1328  		if(is_first == false)
1329  		{
1330  			printf(", ");
1331  		}
1332  		printf("%s", names->pkgname);
1333  		names = names->next;
1334  		is_first = false;
1335  	}
1336  
1337  	printf("\n");
1338  
1339  	printf("\tPackage id: %s\n", entry->id);
1340  	printf("\tPackage description: %s\n", entry->desc);
1341  	printf("\tPackage url: %s\n", entry->url);
1342  
1343  	strftime(buf, sizeof(buf), "%d %b %Y %H:%M", &entry->discovery);
1344  	printf("\tEntry discovered: %s\n", buf);
1345  
1346  	strftime(buf, sizeof(buf), "%d %b %Y %H:%M", &entry->published);
1347  	printf("\tEntry published: %s\n", buf);
1348  
1349  	strftime(buf, sizeof(buf), "%d %b %Y %H:%M", &entry->modified);
1350  	printf("\tEntry modified: %s\n", buf);
1351  
1352  	printf("Vulnerable versions:\n");
1353  
1354  	versions = entry->versions;
1355  
1356  	while(versions)
1357  	{
1358  		pkg_osvf_print_version_type(versions);
1359  		pkg_osvf_print_version(&versions->v1);
1360  		pkg_osvf_print_version(&versions->v2);
1361  		versions = versions->next;
1362  	}
1363  
1364  	printf("Vulnerable packages:\n");
1365  
1366  	packages = entry->packages;
1367  
1368  	while(packages)
1369  	{
1370  		printf("\tPackage name: %s\n", packages->names->pkgname);
1371  		pkg_osvf_print_ecosystem(packages->ecosystem);
1372  		versions = packages->versions;
1373  
1374  		while(versions)
1375  		{
1376  			pkg_osvf_print_version_type(versions);
1377  			pkg_osvf_print_version(&versions->v1);
1378  			pkg_osvf_print_version(&versions->v2);
1379  			versions = versions->next;
1380  		}
1381  
1382  		packages = packages->next;
1383  	}
1384  
1385  	printf("Vulnerability references:\n");
1386  
1387  	references = entry->references;
1388  
1389  	while(references)
1390  	{
1391  		switch(references->type)
1392  		{
1393  		case OSVF_REFERENCE_UNKNOWN:
1394  			printf("\tUNKNOWN: %s\n", references->url);
1395  			break;
1396  		case OSVF_REFERENCE_ADVISORY:
1397  			printf("\tADVISORY: %s\n", references->url);
1398  			break;
1399  		case OSVF_REFERENCE_ARTICLE:
1400  			printf("\tARTICLE: %s\n", references->url);
1401  			break;
1402  		case OSVF_REFERENCE_DETECTION:
1403  			printf("\tDETECTION: %s\n", references->url);
1404  			break;
1405  		case OSVF_REFERENCE_DISCUSSION:
1406  			printf("\tDISCUSSIONn: %s\n", references->url);
1407  			break;
1408  		case OSVF_REFERENCE_REPORT:
1409  			printf("\tREPORT: %s\n", references->url);
1410  			break;
1411  		case OSVF_REFERENCE_FIX:
1412  			printf("\tFIX: %s\n", references->url);
1413  			break;
1414  		case OSVF_REFERENCE_INTRODUCED:
1415  			printf("\tINTRODUCED: %s\n", references->url);
1416  			break;
1417  		case OSVF_REFERENCE_PACKAGE:
1418  			printf("\tPACKAGE: %s\n", references->url);
1419  			break;
1420  		case OSVF_REFERENCE_EVIDENCE:
1421  			printf("\tEVIDENCE: %s\n", references->url);
1422  			break;
1423  		case OSVF_REFERENCE_WEB:
1424  			printf("\tWEB: %s\n", references->url);
1425  			break;
1426  		}
1427  		references = references->next;
1428  	}
1429  }
1430  
1431  struct pkg_audit_entry *
1432  pkg_osvf_create_entry(ucl_object_t *osvf_obj)
1433  {
1434  	struct pkg_audit_entry *entry = NULL;
1435  	struct pkg_audit_package *packages = NULL;
1436  	struct pkg_audit_pkgname *names = NULL;
1437  	struct pkg_audit_versions_range *versions = NULL;
1438  	const ucl_object_t *sub_obj = NULL;
1439  	/* Date format is in RFC3339 */
1440  	const char *date_time_str = "%Y-%m-%dT%H:%M:%SZ";
1441  
1442  	entry = pkg_osvf_new_entry();
1443  
1444  	/* We are probably out of memory or JSON does not exist */
1445  	if(osvf_obj == NULL || entry == NULL)
1446  	{
1447  		return NULL;
1448  	}
1449  
1450  	memset(&entry->modified, 0, sizeof(struct tm));
1451  	memset(&entry->published, 0, sizeof(struct tm));
1452  	memset(&entry->discovery, 0, sizeof(struct tm));
1453  
1454  	/* Data has been validated on load so we can assume
1455  	   there is all needed information */
1456  
1457  	entry->id = xstrdup(pkg_osvf_ucl_string(osvf_obj, "id"));
1458  	entry->desc = xstrdup(pkg_osvf_ucl_string(osvf_obj, "summary"));
1459  
1460  	sub_obj = ucl_object_find_key(osvf_obj, "affected");
1461  
1462  	if(sub_obj && ucl_object_type(sub_obj) == UCL_ARRAY)
1463  	{
1464  		pkg_osvf_parse_affected(entry, ucl_object_find_key(osvf_obj, "affected"));
1465  	}
1466  	else
1467  	{
1468  		return NULL;
1469  	}
1470  
1471  	sub_obj = ucl_object_find_key(osvf_obj, "references");
1472  
1473  	if(sub_obj && ucl_object_type(sub_obj) == UCL_ARRAY)
1474  	{
1475  		pkg_osvf_parse_references(entry, ucl_object_find_key(osvf_obj, "references"));
1476  	}
1477  	else
1478  	{
1479  		return NULL;
1480  	}
1481  
1482  	entry->url = entry->references->url;
1483  
1484  	packages = entry->packages;
1485  	names = entry->names;
1486  	versions = entry->versions;
1487  
1488  	names->pkgname = packages->names->pkgname;
1489  	pkg_osvf_append_version_range(versions, packages->versions);
1490  
1491  	while(packages->next)
1492  	{
1493  		packages = packages->next;
1494  		names->next = xcalloc(1, sizeof(struct pkg_audit_pkgname));
1495  		names = names->next;
1496  		names->pkgname = packages->names->pkgname;
1497  		versions->next = xcalloc(1, sizeof(struct pkg_audit_versions_range));
1498  		versions = versions->next;
1499  		pkg_osvf_append_version_range(versions, packages->versions);
1500  	}
1501  
1502  	entry->pkgname = entry->names->pkgname;
1503  
1504  	if(ucl_object_find_key(osvf_obj, "modified"))
1505  	{
1506  		strptime(ucl_object_tostring(ucl_object_find_key(osvf_obj, "modified")), date_time_str, &entry->modified);
1507  	}
1508  
1509  	if(ucl_object_find_key(osvf_obj, "published"))
1510  	{
1511  		strptime(ucl_object_tostring(ucl_object_find_key(osvf_obj, "published")), date_time_str, &entry->published);
1512  	}
1513  
1514  	if(ucl_object_find_key(osvf_obj, "database_specific"))
1515  	{
1516  		sub_obj = ucl_object_find_key(osvf_obj, "database_specific");
1517  		if(ucl_object_find_key(sub_obj, "discovery"))
1518  		{
1519  			strptime(ucl_object_tostring(ucl_object_find_key(sub_obj, "discovery")), date_time_str, &entry->discovery);
1520  		}
1521  	}
1522  
1523  	return entry;
1524  }