Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_osvf.c
2645 views
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 <[email protected]> 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
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
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()
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
struct pkg_audit_entry *
634
pkg_osvf_new_entry()
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
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
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
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
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
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
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
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
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
const char *
936
pkg_osvf_ucl_string(const ucl_object_t *obj, 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
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
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
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
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
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
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
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
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
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
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
}
1525
1526