/**
 * Canonical security aggregator (G_v4_8 / 2.4.7 Phase C1).
 *
 * One source of truth for "code findings by severity", "dependency
 * advisories by severity", "unique advisory count", and cross-tool
 * dedup. Every consumer (standalone vuln-scan, health-side scorer,
 * BoM, dashboard) reads from `SecurityAggregate` instead of
 * re-counting raw envelope arrays.
 *
 * The disease this closes (D086 / D087 / D091):
 *
 *   - **D086** Health Security section and standalone vuln-scan Code
 *     Findings table both reported "code findings by severity" but
 *     came up with different numbers (`0C 11H 18M 0L` vs
 *     `0C 17H 14M 0L`) on the same repo. Two consumers, two
 *     aggregation paths, slightly-different inclusion rules.
 *
 *   - **D087** Vuln-scan exec summary said "Subtotal: 70" (sum of
 *     dep-vuln severity buckets) and the same page later said
 *     "81 advisories" (findings.length). 70 vs 81 on one page.
 *
 *   - **D091** A single TLS-bypass root finding surfaced twice in the
 *     Code Findings table (registry-grep at `:74` HIGH, semgrep at
 *     `:72` MEDIUM) because code findings carried no fingerprint and
 *     no cross-tool dedup ran.
 *
 * Architectural posture:
 *
 *   - The aggregator sits BETWEEN gather and reports. Gather still
 *     produces raw envelopes (`gatherSecrets`, `gatherFileFindings`,
 *     `gatherCodePatterns`, `gatherTlsBypassFindings`, `gatherDepVulns`);
 *     the aggregator merges + dedups + buckets them into the canonical
 *     shape; consumers read by field name.
 *
 *   - Three separately-named severity buckets (`codeBySeverity`,
 *     `depBySeverity`, `secretsBySeverity`) — the shape forbids any
 *     consumer from accidentally summing cross-axis again.
 *
 *   - Two named dep counts (`dependencyAdvisoryUniqueCount` for the
 *     canonical user-facing total; `dependencyFindingsRawCount` for
 *     diagnostic audit). Renderers cannot pick "the wrong number"
 *     without naming which they want.
 *
 *   - Code findings get a canonical-rule + line-window fingerprint;
 *     cross-tool collisions collapse to ONE CodeFinding with
 *     `keptSeverity = max(severities)` and `producedBy` listing all
 *     contributing tools. The `dedupCollisions` audit trail records
 *     every collapse for `--detailed` visibility.
 *
 *   - `provenance` distinguishes "tool ran, 0 findings" from "tool
 *     didn't run" — drives D080-style "(not run: typescript)" labels.
 *
 * G_v4_8 architectural gate (`scripts/check-architecture.sh`) blocks
 * `countBySeverity` / severity-Record accumulator declarations
 * outside this file, mirroring G_v4_7's walker allowlist.
 */
import type { DepVulnFinding } from '../../languages/capabilities/types';
import type { Severity, SecurityFinding } from './types';
export type { Severity, FindingCategory, SecurityFinding } from './types';
/**
 * Per-severity counts. Local copy (avoids cross-module import friction
 * with `capabilities/types.SeverityCounts` — same shape, different
 * module home).
 */
export interface SeverityCounts {
    critical: number;
    high: number;
    medium: number;
    low: number;
}
/**
 * Post-aggregation code/secret/config finding. Extends raw
 * `SecurityFinding` with the identity + provenance fields the
 * aggregator stamps:
 *
 *   - `fingerprint` — stable 16-char hash of
 *     `(canonicalRule | file | lineWindow)`. Same key across runs;
 *     enables diff tooling and dedup-by-identity downstream.
 *   - `canonicalRule` — normalized rule id from the canonical-rule
 *     registry. Different raw tool/rule pairs that describe the same
 *     root finding collapse to the same `canonicalRule`. Unmapped
 *     pairs pass through as `raw:${tool}:${rule}` — conservative
 *     default; new collapse rules require explicit registry entries.
 *   - `producedBy` — every raw `tool` that contributed to this
 *     finding. Length > 1 means cross-tool dedup fired.
 */
export interface CodeFinding extends SecurityFinding {
    fingerprint: string;
    canonicalRule: string;
    producedBy: string[];
}
/**
 * One collapsed-collision audit entry. Renders in `--detailed` so a
 * security reviewer can verify "yes, these two tools agreed on this
 * one finding" rather than wondering why a tool's findings count
 * differs from the report's surface count.
 */
export interface DedupCollision {
    canonicalRule: string;
    file: string;
    line: number;
    keptSeverity: Severity;
    collapsedFrom: ReadonlyArray<{
        tool: string;
        rule: string;
        line: number;
        severity: Severity;
    }>;
}
/**
 * Per-tool run-state provenance. Distinguishes "tool ran with zero
 * findings" from "tool didn't run." Drives the "(not run: …)" labels
 * on report tool-attribution lines and the security scorer's "scan
 * was partial" cap signal (D025e / D080 lineage).
 */
export interface AggregateProvenance {
    secrets: {
        tool: string | null;
        ran: boolean;
    };
    codePatterns: {
        tool: string | null;
        ran: boolean;
    };
    tlsBypass: {
        ran: boolean;
        patternCount: number;
    };
    fileFindings: {
        ran: boolean;
    };
    depVulns: {
        tool: string | null;
        available: boolean;
        unavailableReason: string;
    };
}
/**
 * The canonical security aggregate. Every consumer reads from this;
 * consumers MUST NOT re-aggregate from raw envelope arrays. The
 * G_v4_8 architectural gate enforces this at commit time.
 */
export interface SecurityAggregate {
    /** Code-pattern findings by severity (semgrep + tls-bypass-registry
     *  + any future per-pack code-pattern producers), post-dedup. */
    codeBySeverity: SeverityCounts;
    /** Dependency advisories by severity, derived from the
     *  fingerprint-unique advisory set (NOT the per-pack envelope
     *  count sum). Sums to `dependencyAdvisoryUniqueCount`. */
    depBySeverity: SeverityCounts;
    /** Secret + secret-adjacent findings (gitleaks + private-key files +
     *  .env-in-git) by severity. Each axis stays separate so consumers
     *  pick which they own. */
    secretsBySeverity: SeverityCounts;
    /** Findings partitioned by category, post-dedup. Renderers iterate
     *  these — never iterate raw envelope arrays. `dependency` is the
     *  fingerprint-unique advisory set. */
    findingsByCategory: {
        secret: ReadonlyArray<CodeFinding>;
        code: ReadonlyArray<CodeFinding>;
        config: ReadonlyArray<CodeFinding>;
        dependency: ReadonlyArray<DepVulnFinding>;
    };
    /**
     * The canonical user-facing advisory count: unique fingerprints
     * across every pack's findings. Use for the vuln-scan "Subtotal:"
     * line, BoM `totalAdvisories`, and the "Showing N of M" denominator.
     * Aligns with BoM's existing fingerprint-unique semantics (D076 /
     * D085) so vuln-scan and BoM never report different totals.
     */
    dependencyAdvisoryUniqueCount: number;
    /**
     * Diagnostic-only: per-pack envelope.findings.length sum BEFORE
     * fingerprint dedup. Surfaces in `--detailed` audit logs so the
     * delta vs `dependencyAdvisoryUniqueCount` is visible. Renderers
     * should NOT use this for any user-facing count.
     */
    dependencyFindingsRawCount: number;
    /** Audit trail of every cross-tool / cross-line-window collapse.
     *  Empty in the no-collision case. */
    dedupCollisions: ReadonlyArray<DedupCollision>;
    /** Per-source provenance — drives "(not run: typescript)" labels. */
    provenance: AggregateProvenance;
}
/**
 * Input envelopes for `buildSecurityAggregate`. Each field maps 1:1
 * to a `gather*` function in `security/gather.ts`. The aggregator
 * never invokes I/O; it's a pure function over already-gathered
 * data, which keeps unit testing trivial.
 */
export interface SecurityAggregateInput {
    secrets: {
        findings: SecurityFinding[];
        toolUsed: string | null;
    };
    fileFindings: SecurityFinding[];
    codePatterns: {
        findings: SecurityFinding[];
        toolUsed: string | null;
    };
    tlsBypass: SecurityFinding[];
    /** Pattern count from `allTlsBypassPatterns()` — drives the
     *  `provenance.tlsBypass.ran` flag (ran=false when no patterns were
     *  registered, NOT when 0 findings matched against a non-empty
     *  pattern set). */
    tlsBypassPatternCount: number;
    depVulns: {
        findings: DepVulnFinding[];
        tool: string | null;
        available: boolean;
        unavailableReason: string;
    };
}
/**
 * Build the canonical aggregate from per-gatherer envelopes. Pure
 * function — same input always produces the same output.
 *
 * Dedup pipeline (code-side):
 *   1. Concat raw findings from secrets/fileFindings/codePatterns/tlsBypass.
 *   2. Group by `(canonicalRule, file, lineWindow)` key.
 *   3. For each group:
 *      - Emit ONE `CodeFinding` with `keptSeverity = max(severities)`,
 *        `producedBy` = unique sources.
 *      - If the group had >1 raw finding, record a `DedupCollision`
 *        audit entry.
 *
 * Dedup pipeline (dep-side):
 *   - Group `depVulns.findings` by `fingerprint`.
 *   - For each group: pick the highest-severity entry as the
 *     representative; severity counts are derived from the unique
 *     set so they match `dependencyAdvisoryUniqueCount`.
 *   - Findings without a fingerprint pass through unchanged (defensive;
 *     `stampFingerprints` in `gatherDepVulns` runs before this).
 */
export declare function buildSecurityAggregate(input: SecurityAggregateInput): SecurityAggregate;
//# sourceMappingURL=aggregator.d.ts.map