|
11 | 11 | from urllib.parse import urlencode |
12 | 12 |
|
13 | 13 | from django.contrib.postgres.aggregates import ArrayAgg |
14 | | -from django.contrib.postgres.aggregates import JSONBAgg |
15 | 14 | from django.db.models import Exists |
16 | 15 | from django.db.models import F |
17 | 16 | from django.db.models import Max |
18 | 17 | from django.db.models import OuterRef |
19 | 18 | from django.db.models import Prefetch |
20 | 19 | from django.db.models import Q |
21 | | -from django.db.models.functions import JSONObject |
22 | 20 | from django_filters import rest_framework as filters |
23 | 21 | from drf_spectacular.utils import extend_schema |
24 | 22 | from packageurl import PackageURL |
@@ -458,71 +456,94 @@ class AffectedByAdvisoriesViewSet(PackageAdvisoriesViewSet): |
458 | 456 |
|
459 | 457 | def get_patches_bulk(package_ids, advisory_ids): |
460 | 458 | """Get introduced and fixed patches""" |
461 | | - patches_query = ( |
462 | | - ImpactedPackageAffecting.objects.filter( |
463 | | - package_id__in=package_ids, |
464 | | - impacted_package__advisory_id__in=advisory_ids, |
465 | | - impacted_package__advisory__is_latest=True, |
466 | | - impacted_package__advisory___all_impacts_unfurled_at__isnull=False, |
467 | | - ) |
468 | | - .filter( |
469 | | - Q(impacted_package__introduced_by_package_commit_patches__isnull=False) |
470 | | - | Q(impacted_package__fixed_by_package_commit_patches__isnull=False) |
471 | | - ) |
472 | | - .values( |
473 | | - "package_id", |
474 | | - "impacted_package__advisory_id", |
475 | | - introduced_commit_hash=F( |
476 | | - "impacted_package__introduced_by_package_commit_patches__commit_hash" |
477 | | - ), |
478 | | - introduced_vcs_url=F("impacted_package__introduced_by_package_commit_patches__vcs_url"), |
479 | | - fixed_commit_hash=F("impacted_package__fixed_by_package_commit_patches__commit_hash"), |
480 | | - fixed_vcs_url=F("impacted_package__fixed_by_package_commit_patches__vcs_url"), |
481 | | - ) |
482 | | - .distinct() |
483 | | - .order_by() |
| 459 | + |
| 460 | + base_qs = ImpactedPackageAffecting.objects.filter( |
| 461 | + package_id__in=package_ids, |
| 462 | + impacted_package__advisory_id__in=advisory_ids, |
| 463 | + impacted_package__advisory__is_latest=True, |
| 464 | + impacted_package__advisory___all_impacts_unfurled_at__isnull=False, |
| 465 | + ) |
| 466 | + |
| 467 | + introduced_rows = base_qs.filter( |
| 468 | + impacted_package__introduced_by_package_commit_patches__isnull=False |
| 469 | + ).values( |
| 470 | + "package_id", |
| 471 | + "impacted_package__advisory_id", |
| 472 | + commit_hash=F("impacted_package__introduced_by_package_commit_patches__commit_hash"), |
| 473 | + vcs_url=F("impacted_package__introduced_by_package_commit_patches__vcs_url"), |
| 474 | + ) |
| 475 | + |
| 476 | + fixed_rows = base_qs.filter( |
| 477 | + impacted_package__fixed_by_package_commit_patches__isnull=False |
| 478 | + ).values( |
| 479 | + "package_id", |
| 480 | + "impacted_package__advisory_id", |
| 481 | + commit_hash=F("impacted_package__fixed_by_package_commit_patches__commit_hash"), |
| 482 | + vcs_url=F("impacted_package__fixed_by_package_commit_patches__vcs_url"), |
484 | 483 | ) |
485 | 484 |
|
486 | 485 | introduced_patches_map = defaultdict(list) |
487 | 486 | fixed_patches_map = defaultdict(list) |
488 | 487 |
|
489 | | - for row in patches_query: |
490 | | - key = (row["package_id"], row["impacted_package__advisory_id"]) |
491 | | - |
492 | | - if row["introduced_commit_hash"] or row["introduced_vcs_url"]: |
| 488 | + for row in introduced_rows: |
| 489 | + if row["commit_hash"] or row["vcs_url"]: |
| 490 | + key = (row["package_id"], row["impacted_package__advisory_id"]) |
493 | 491 | introduced_patches_map[key].append( |
494 | 492 | { |
495 | | - "commit_hash": row["introduced_commit_hash"], |
496 | | - "vcs_url": row["introduced_vcs_url"], |
| 493 | + "commit_hash": row["commit_hash"], |
| 494 | + "vcs_url": row["vcs_url"], |
497 | 495 | } |
498 | 496 | ) |
499 | 497 |
|
500 | | - if row["fixed_commit_hash"] or row["fixed_vcs_url"]: |
| 498 | + for row in fixed_rows: |
| 499 | + if row["commit_hash"] or row["vcs_url"]: |
| 500 | + key = (row["package_id"], row["impacted_package__advisory_id"]) |
501 | 501 | fixed_patches_map[key].append( |
502 | 502 | { |
503 | | - "commit_hash": row["fixed_commit_hash"], |
504 | | - "vcs_url": row["fixed_vcs_url"], |
| 503 | + "commit_hash": row["commit_hash"], |
| 504 | + "vcs_url": row["vcs_url"], |
505 | 505 | } |
506 | 506 | ) |
507 | 507 |
|
508 | 508 | return introduced_patches_map, fixed_patches_map |
509 | 509 |
|
510 | 510 |
|
511 | | -def collect_patches(patches_map, package_id, advisory_ids): |
512 | | - """Merge and dedupe patch entries from patches_map for all advisory_ids_set""" |
513 | | - seen = set() |
514 | | - collected = [] |
515 | | - if not patches_map or not advisory_ids or not package_id: |
516 | | - return [] |
| 511 | +def build_patch_set_map(patches_map, advisory_ids_by_set): |
| 512 | + """ |
| 513 | + Returns: |
| 514 | + { |
| 515 | + advisory_set_id: [ |
| 516 | + { |
| 517 | + "commit_hash": "...", |
| 518 | + "vcs_url": "...", |
| 519 | + } |
| 520 | + ] |
| 521 | + } |
| 522 | + """ |
| 523 | + result = {} |
| 524 | + |
| 525 | + for advisory_set_id, advisory_ids in advisory_ids_by_set.items(): |
| 526 | + seen = set() |
| 527 | + collected = [] |
517 | 528 |
|
518 | | - for advisory_id in advisory_ids: |
519 | | - for patch in patches_map.get((package_id, advisory_id), []): |
520 | | - patch_key = (patch["commit_hash"], patch["vcs_url"]) |
521 | | - if patch_key in seen: |
| 529 | + for (package_id, advisory_id), patches in patches_map.items(): |
| 530 | + if advisory_id not in advisory_ids: |
522 | 531 | continue |
523 | | - seen.add(patch_key) |
524 | | - collected.append(patch) |
525 | | - return collected |
| 532 | + |
| 533 | + for patch in patches: |
| 534 | + key = ( |
| 535 | + patch["commit_hash"], |
| 536 | + patch["vcs_url"], |
| 537 | + ) |
| 538 | + |
| 539 | + if key in seen: |
| 540 | + continue |
| 541 | + |
| 542 | + seen.add(key) |
| 543 | + collected.append(patch) |
| 544 | + |
| 545 | + result[advisory_set_id] = collected |
| 546 | + return result |
526 | 547 |
|
527 | 548 |
|
528 | 549 | def get_affected_advisories_bulk(packages, max_advisories, base_url, reachability=False): |
@@ -618,13 +639,23 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url, reachabilit |
618 | 639 | advisory_ids_by_set[advisory_set_id].add(advisory_id) |
619 | 640 | all_advisory_ids.add(advisory_id) |
620 | 641 |
|
| 642 | + introduced_patches_by_set = {} |
| 643 | + fixed_patches_by_set = {} |
621 | 644 | if reachability: |
622 | 645 | introduced_patches_map, fixed_patches_map = get_patches_bulk( |
623 | 646 | package_ids, |
624 | 647 | advisory_ids=all_advisory_ids, |
625 | 648 | ) |
626 | | - else: |
627 | | - introduced_patches_map, fixed_patches_map = {}, {} |
| 649 | + |
| 650 | + introduced_patches_by_set = build_patch_set_map( |
| 651 | + introduced_patches_map, |
| 652 | + advisory_ids_by_set, |
| 653 | + ) |
| 654 | + |
| 655 | + fixed_patches_by_set = build_patch_set_map( |
| 656 | + fixed_patches_map, |
| 657 | + advisory_ids_by_set, |
| 658 | + ) |
628 | 659 |
|
629 | 660 | ssvc_rows = ( |
630 | 661 | SSVC.objects.filter( |
@@ -730,11 +761,13 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url, reachabilit |
730 | 761 | "exploitability": exploitability, |
731 | 762 | "risk_score": risk_score, |
732 | 763 | "fixed_by_packages": fixed_by_packages, |
733 | | - "introduced_in_patch": collect_patches( |
734 | | - introduced_patches_map, package.id, advisory_ids_by_set.get(adv.id, {}) |
| 764 | + "introduced_in_patch": introduced_patches_by_set.get( |
| 765 | + adv.id, |
| 766 | + [], |
735 | 767 | ), |
736 | | - "fixed_in_patch": collect_patches( |
737 | | - fixed_patches_map, package.id, advisory_ids_by_set.get(adv.id, {}) |
| 768 | + "fixed_in_patch": fixed_patches_by_set.get( |
| 769 | + adv.id, |
| 770 | + [], |
738 | 771 | ), |
739 | 772 | "ssvc_trees": adv.ssvc_trees, |
740 | 773 | "resource_url": resource_url, |
@@ -831,10 +864,8 @@ def get_affected_advisories_bulk(packages, max_advisories, base_url, reachabilit |
831 | 864 | "exploitability": advisory.exploitability, |
832 | 865 | "risk_score": advisory.risk_score, |
833 | 866 | "fixed_by_packages": fixed_by_packages, |
834 | | - "introduced_in_patch": introduced_patches_map.get( |
835 | | - (package.id, advisory_id), [] |
836 | | - ), |
837 | | - "fixed_in_patch": fixed_patches_map.get((package.id, advisory_id), []), |
| 867 | + "introduced_in_patch": introduced_patches_by_set.get(advisory_id, []), |
| 868 | + "fixed_in_patch": introduced_patches_by_set.get(advisory_id, []), |
838 | 869 | "ssvc_trees": [ |
839 | 870 | { |
840 | 871 | "vector": ssvc.vector, |
|
0 commit comments