FindingsListComponent

Comprehensive findings list with Evidence-Weighted Score (EWS) integration, filtering, and bulk selection.

Overview

The FindingsListComponent displays a sortable, filterable table of vulnerability findings with integrated score loading and display.

Selector

<app-findings-list />

Inputs

InputTypeDefaultDescription
findingsFinding[][]Array of findings to display
autoLoadScoresbooleantrueAuto-fetch scores when findings change

Outputs

OutputTypeDescription
findingSelectEventEmitter<ScoredFinding>Emits when a finding row is clicked
selectionChangeEventEmitter<string[]>Emits finding IDs when selection changes

Data Structures

Finding

interface Finding {
  id: string;               // Unique finding ID
  advisoryId: string;       // CVE/GHSA ID
  packageName: string;
  packageVersion: string;
  severity: 'critical' | 'high' | 'medium' | 'low';
  status: 'open' | 'in_progress' | 'fixed' | 'excepted';
  publishedAt?: string;     // ISO 8601
}

ScoredFinding

interface ScoredFinding extends Finding {
  score?: EvidenceWeightedScoreResult;
  scoreLoading: boolean;
}

Table Columns

ColumnDescriptionSortable
CheckboxBulk selectionNo
ScoreEWS score pill with flagsYes
AdvisoryCVE/GHSA identifierYes
PackagePackage name and versionYes
SeverityCVSS severity levelYes
StatusFinding statusYes

Features

Score Loading

When autoLoadScores is true, scores are fetched automatically via the SCORING_API injection token.

Bucket Filtering

Filter findings by priority bucket using the chip filters:

Flag Filtering

Filter by active score flags:

Row Visual Indicators

Hard Fail Rows

Findings with hard-fail flag are highlighted with:

Anchored Rows

Findings with anchored flag display:

Note: If a finding is both anchored and hard-fail, hard-fail styling takes visual precedence.

Text search across advisory ID and package name.

Sorting

Click column headers to sort. Click again to reverse order.

Bulk Selection

Usage Examples

Basic Usage

<app-findings-list
  [findings]="findings"
  (findingSelect)="openFinding($event)"
/>

Without Auto-Loading Scores

<app-findings-list
  [findings]="findings"
  [autoLoadScores]="false"
/>

With Selection Handling

<app-findings-list
  [findings]="findings"
  [autoLoadScores]="true"
  (selectionChange)="onSelectionChange($event)"
/>

<div class="bulk-actions" *ngIf="selectedIds.length > 0">
  <button (click)="acknowledgeSelected()">
    Acknowledge ({{ selectedIds.length }})
  </button>
</div>
selectedIds: string[] = [];

onSelectionChange(ids: string[]): void {
  this.selectedIds = ids;
}

Full Feature Example

@Component({
  selector: 'app-vulnerability-dashboard',
  template: `
    <div class="dashboard">
      <h1>Vulnerabilities</h1>

      <app-findings-list
        [findings]="findings()"
        [autoLoadScores]="true"
        (findingSelect)="openFindingDetail($event)"
        (selectionChange)="updateSelection($event)"
      />

      @if (selectedFinding()) {
        <app-finding-detail-panel
          [finding]="selectedFinding()"
          (close)="closeFindingDetail()"
        />
      }
    </div>
  `
})
export class VulnerabilityDashboardComponent {
  findings = signal<Finding[]>([]);
  selectedFinding = signal<ScoredFinding | null>(null);
  selectedIds = signal<string[]>([]);

  constructor(private findingsService: FindingsService) {
    this.loadFindings();
  }

  async loadFindings(): Promise<void> {
    this.findings.set(await this.findingsService.getFindings());
  }

  openFindingDetail(finding: ScoredFinding): void {
    this.selectedFinding.set(finding);
  }

  closeFindingDetail(): void {
    this.selectedFinding.set(null);
  }

  updateSelection(ids: string[]): void {
    this.selectedIds.set(ids);
  }
}

Dependency Injection

The component requires a SCORING_API provider:

import { SCORING_API, ScoringApiService } from '@app/core/services/scoring.service';

@NgModule({
  providers: [
    { provide: SCORING_API, useClass: ScoringApiService }
  ]
})
export class AppModule {}

Mock API for Testing

import { FixtureScoringApi } from '@app/core/testing/scoring.testing';

TestBed.configureTestingModule({
  providers: [
    { provide: SCORING_API, useClass: FixtureScoringApi }
  ]
});

Empty States

No Findings

<!-- Displays: "No findings to display" -->
<app-findings-list [findings]="[]" />

No Matches

When filters result in no matches:

<!-- Displays: "No findings match the current filters" -->

Accessibility

Styling

app-findings-list {
  --table-header-bg: #f9fafb;
  --table-border-color: #e5e7eb;
  --table-row-hover: #f3f4f6;
  --table-row-selected: #eff6ff;
}