@@ -1342,8 +1342,8 @@ osv_scan(#{version := Version, sarif := Sarif}) ->
1342
1342
[];
1343
1343
_ ->
1344
1344
NameVulnerabilities = lists :zip (osv_names (OSVQuery ), OSVResults ),
1345
- lists :filtermap (fun ({Name , #{~ " vulns" := Ids }}) ->
1346
- {true , {Name , [Id || #{~ " id" := Id } <- Ids ]}};
1345
+ lists :filtermap (fun ({NameVersion , #{~ " vulns" := Ids }}) ->
1346
+ {true , {NameVersion , [Id || #{~ " id" := Id } <- Ids ]}};
1347
1347
(_ ) ->
1348
1348
false
1349
1349
end , NameVulnerabilities )
@@ -1352,24 +1352,24 @@ osv_scan(#{version := Version, sarif := Sarif}) ->
1352
1352
{error , [URI , Error ]}
1353
1353
end ,
1354
1354
Vulns1 = ignore_vex_cves (Vulns ),
1355
- ok = generate_sarif (Sarif , Vulns1 ),
1355
+ ok = generate_sarif (Version , Sarif , Vulns1 ),
1356
1356
FormattedVulns = format_vulnerabilities (Vulns1 ),
1357
1357
report_vulnerabilities (FormattedVulns ).
1358
1358
1359
- generate_sarif (false , _Vulns ) ->
1359
+ generate_sarif (_ , false , _Vulns ) ->
1360
1360
io :format (" [SARIF] No sarif file generated~n~n " ),
1361
1361
ok ;
1362
- generate_sarif (true , Vulns ) ->
1362
+ generate_sarif (Branch , true , Vulns ) ->
1363
1363
SarifFilename = " results.sarif" ,
1364
1364
1365
1365
{ok , Cwd } = file :get_cwd (),
1366
1366
io :format (" [SARIF] Generating Sarif: ~s~n " , [Cwd ++ " /" ++ SarifFilename ]),
1367
1367
io :format (" ok~n~n " ),
1368
1368
1369
- Sarif = json :format (generate_sarif (Vulns )),
1369
+ Sarif = json :format (generate_sarif (Branch , Vulns )),
1370
1370
file :write_file (SarifFilename , Sarif ).
1371
1371
1372
- generate_sarif (Vulns ) ->
1372
+ generate_sarif (Branch , Vulns ) ->
1373
1373
#{ ~ " version" => ~ " 2.1.0" ,
1374
1374
~ " $schema" => ~ " https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json" ,
1375
1375
~ " runs" =>
@@ -1396,39 +1396,48 @@ generate_sarif(Vulns) ->
1396
1396
~ " ruleId" => ~ " CVE-OTP-VENDOR" ,
1397
1397
~ " ruleIndex" => 0 , % matches rule object that should apply
1398
1398
~ " level" => ~ " warning" ,
1399
- ~ " message" => #{ ~ " text" => error_to_text ({Dependency , CVE }) },
1399
+ ~ " message" => #{
1400
+ ~ " text" => error_to_text (Branch , Dependency , Version , CVE )
1401
+ },
1400
1402
~ " locations" =>
1401
1403
[ #{ ~ " physicalLocation" =>
1402
1404
#{ ~ " artifactLocation" =>
1403
1405
#{ ~ " uri" => Dependency }}}
1404
- ]
1405
- } || {Dependency , CVEs } <- Vulns , CVE <- CVEs ],
1406
+ ],
1407
+ ~ " partialFingerprints" =>
1408
+ #{ Branch => calculate_fingerprint (Branch , Dependency , Version , CVE )}
1409
+ } || {Dependency , Version , CVEs } <- Vulns , CVE <- CVEs ],
1406
1410
~ " artifacts" =>
1407
1411
[ #{ ~ " location" => #{ ~ " uri" => Dependency },
1408
1412
~ " length" => - 1
1409
- } || {Dependency , _ } <- Vulns ]
1413
+ } || {Dependency , _ , _ } <- Vulns ]
1410
1414
}]
1411
1415
}.
1412
1416
1413
- error_to_text ({Dependency , Vuln }) ->
1414
- <<" Dependency " , Dependency /binary , " has " , Vuln /binary >>.
1417
+ error_to_text (Branch , Dependency , Version , Vuln ) ->
1418
+ <<" [" , Branch /binary , " ] Dependency " , Dependency /binary , " in commit/version " , Version /binary ,
1419
+ " has the following detected vulnerability: " , Vuln /binary >>.
1420
+
1421
+ calculate_fingerprint (Branch , Dependency , Version , CVE ) ->
1422
+ Bin = crypto :hash (sha , <<Branch /binary , Dependency /binary , Version /binary , CVE /binary >>),
1423
+ binary :encode_hex (Bin ).
1415
1424
1416
1425
% % TODO: fix by reading VEX files from erlang/vex or repo containing VEX files
1417
1426
ignore_vex_cves (Vulns ) ->
1418
1427
lists :foldl (fun ({~ " github.com/wxWidgets/wxWidgets" , _CVEs }, Acc ) ->
1419
1428
% % OTP cannot be vulnerable to wxwidgets because
1420
1429
% % we only take documentation.
1421
1430
Acc ;
1422
- ({Name , CVEs }, Acc ) ->
1431
+ ({{ Name , Version } , CVEs }, Acc ) ->
1423
1432
case maps :get (Name , non_vulnerable_cves (), not_found ) of
1424
1433
not_found ->
1425
- [{Name , CVEs } | Acc ];
1434
+ [{Name , Version , CVEs } | Acc ];
1426
1435
NonCVEs ->
1427
1436
case CVEs -- NonCVEs of
1428
1437
[] ->
1429
1438
Acc ;
1430
1439
Vs ->
1431
- [{Name , Vs } | Acc ]
1440
+ [{Name , Version , Vs } | Acc ]
1432
1441
end
1433
1442
end
1434
1443
end , [], Vulns ).
@@ -1445,7 +1454,7 @@ non_vulnerable_cves() -> #{}.
1445
1454
format_vulnerabilities ({error , ErrorContext }) ->
1446
1455
{error , ErrorContext };
1447
1456
format_vulnerabilities (ExistingVulnerabilities ) when is_list (ExistingVulnerabilities ) ->
1448
- lists :map (fun ({N , Ids }) ->
1457
+ lists :map (fun ({N , _ , Ids }) ->
1449
1458
io_lib :format (" - ~s : ~s~n " , [N , lists :join (" ," , Ids )])
1450
1459
end , ExistingVulnerabilities ).
1451
1460
@@ -1458,11 +1467,14 @@ report_vulnerabilities(FormatVulns) ->
1458
1467
1459
1468
osv_names (#{~ " queries" := Packages }) ->
1460
1469
lists :map (fun osv_names /1 , Packages );
1461
- osv_names (#{~ " package" := #{~ " name" := Name }}) ->
1462
- Name .
1470
+ osv_names (#{~ " package" := #{~ " name" := Name }, ~ " commit" := Commit }) ->
1471
+ {Name , Commit };
1472
+ osv_names (#{~ " package" := #{~ " name" := Name }, ~ " version" := Version }) ->
1473
+ {Name , Version }.
1474
+
1463
1475
1464
1476
generate_osv_query (Packages ) ->
1465
- #{~ " queries" => lists :foldl (fun generate_osv_query /2 , [], Packages )}.
1477
+ #{~ " queries" => lists :usort ( lists : foldl (fun generate_osv_query /2 , [], Packages ) )}.
1466
1478
generate_osv_query (#{~ " versionInfo" := Vsn , ~ " ecosystem" := Ecosystem , ~ " name" := Name }, Acc ) ->
1467
1479
Package = #{~ " package" => #{~ " name" => Name , ~ " ecosystem" => Ecosystem }, ~ " version" => Vsn },
1468
1480
[Package | Acc ];
0 commit comments