Skip to content

v2.6.0 — per-finding suppression + public-repo-safe baselines

Choose a tag to compare

@siddarthc siddarthc released this 23 May 12:04
· 146 commits to main since this release

The "per-finding suppression + public-repo-safe baselines" release.
Adds the typed-category allowlist surface for false-positive /
test-fixture / mitigated-externally / accepted-risk / deferred
suppression with inline + file-level modes; retires license
findings from the baseline (~73% size drop on real customer repos);
introduces three baseline modes with visibility-aware defaults so
public repos no longer leak file paths, package names, and
advisory IDs through a committed baseline.

Added

  • Per-finding allowlistvyuh-dxkit allowlist add/list/show/audit/prune.
    Typed-category suppression (false-positive, test-fixture,
    mitigated-externally, accepted-risk, deferred) with required
    reason + (where relevant) expiry. Two surfaces: inline
    // dxkit-allow:<category> reason="..." annotations and a
    file-level .dxkit/allowlist.json. accepted-risk and deferred
    require expiry (default 90 days). See
    docs/commands/allowlist.md.
  • Strict stale-annotation detection — orphaned dxkit-allow:
    annotations (where the underlying finding is now gone) emit a
    new stale-allow baseline kind on the next scan. The
    TypeScript @ts-expect-error pattern, applied to suppressions —
    forces cleanup, prevents the annotation graveyard. Allowlisting
    a stale-allow finding is forbidden; only remediation is to
    remove the orphaned comment.
  • Allowlist activity in PR comments — the
    dxkit-guardrails.yml workflow's sticky PR comment now includes
    an "Allowlist activity" section listing every entry added (or
    removed) on this branch versus the baseline commit. Reviewers
    see new suppressions being introduced and can sanity-check
    category + reason + expiry before approving.
  • vyuh-dxkit issue — pre-filled GitHub Issues for false
    positives, missing findings, bugs, feature requests, and docs
    gaps. Nothing submits automatically — the CLI opens the
    customer's browser at a new-issue URL with env metadata
    pre-populated, customer reviews + clicks "Submit." See
    docs/commands/issue.md.
  • commentSyntax on language packs — each pack declares its
    line-comment marker (# for python/ruby; // for
    typescript/go/rust/csharp/kotlin/java). Drives the inline
    allowlist-annotation generator across every language uniformly.
    Recipe-enforced: scaffolder ships an empty placeholder so
    unfilled packs fail the contract test until populated.
  • Three preemptive architecture rules in scripts/check-architecture.sh
    lock down the allowlist canonical entry points: no createHash
    inside src/allowlist/, no direct allowlist.json IO outside
    the canonical loader, no language-comment fallback literals
    (?? '//') anywhere in the module.

Changed

  • License findings retired from the baseline. Per-package
    license attributions no longer flow through the baseline
    producer registry — they were informational, not regression
    material, and dominated baselines on real customer repos
    (~73% of entries). The canonical license inventory now lives
    solely in .dxkit/bom.json (vyuh-dxkit bom), which already
    carries richer per-package data (licenseType, licenseText,
    sourceUrl, supplier, releaseDate). Lenient migration:
    baselines written by older dxkit versions still load — the
    reader silently filters retired license entries on the way
    in (no file rewrite until the next baseline create --force).
    Dependency vulnerability tracking is unchanged — dep-vuln
    is a separate identity kind on a separate producer and still
    blocks via the guardrail check.
  • Sanitization machinery for baseline entries. New pure
    module src/baseline/sanitize.ts introduces a stripped
    SanitizedBaselineEntry variant ({ id, kind, sanitized: true })
    carrying identity + kind only. The sanitizeEntry /
    sanitizeFile pass collapses every rich field; cross-run
    matching still works at full confidence via the fingerprint
    multiset pass. Producers now emit the rich
    RichBaselineEntry shape (a BaselineEntry excluding the
    sanitized variant); sanitization is a write-time
    transformation, never a producer concern. Consumers walking
    a baseline narrow via the isSanitized type guard before
    switching on entry.kind. Write-path wiring + visibility-
    aware mode selection ship in a follow-up commit.

Added

  • Three baseline modes with visibility-aware defaults.
    committed-full (today's behavior, rich entries), committed- sanitized (stripped per-entry payload via the sanitization
    pass), and ref-based (no committed file; guardrail check
    recomputes the prior side from a git ref via git worktree add). The mode is picked by a single resolver
    (src/baseline/modes.ts) with precedence: CLI flag →
    .dxkit/policy.json:baseline.mode → visibility-derived default
    (public repos auto-pick ref-based; everything else picks
    committed-full). committed-sanitized is never auto-picked
    — it's the explicit opt-in for compliance-conscious private
    repos.
  • vyuh-dxkit baseline create [--mode <m>] [--ref <r>] and
    vyuh-dxkit guardrail check [--mode <m>] [--ref <r>] — flags
    override policy.json for one-off runs.
  • gh repo view --json visibility probe + per-process cache
    in src/baseline/visibility.ts. Every failure path returns
    'unknown'; the resolver treats unknown as private to avoid
    surprise sanitization when gh auth lapses.
  • Ref-based gather mechanics in src/baseline/ref-baseline.ts
    withRefWorktree(opts, fn) is the reusable primitive; tears
    down the worktree on success + failure. Mirrors file-mode
    .dxkit/salt into the worktree so secret-HMAC entries pair
    across cwd + worktree.

Architectural notes

  • New CLAUDE.md rule 11: baseline mode resolution flows through
    resolveBaselineMode. Two arch-check rules lock the contract:
    no gh repo view --json visibility outside
    src/baseline/visibility.ts; no git worktree add / remove
    outside src/baseline/ref-baseline.ts.
  • resolvePolicy lifted from check.ts to policy.ts so
    createBaseline and runGuardrailCheck share one canonical
    loader.

Discovery surfaces

  • PR-comment markdown now shows the resolved baseline mode in
    the sticky footer (_Mode_: \ref-based` (ref: `origin/main`)`).
    Reviewers see WHY a guardrail run picked a given posture.
  • JSON renderer carries baseline.mode = { value, source, explanation, ref? } so agents + dashboards can read the audit
    trail without re-deriving it.
  • vyuh-dxkit doctor has two new operational checks:
    • "baseline mode: ref-based" / "baseline captured (mode: ...)" —
      the existing baseline-captured check now understands ref-based
      mode (where no on-disk file is expected) so the doctor stops
      reporting a false-negative on public repos.
    • "baseline mode aligned with repo visibility" — warns when an
      explicit committed-full pin is in use on a public repo (the
      posture leaks file paths + package names; the auto-picker
      would have chosen ref-based).
  • dxkit-onboard skill — step 5 now ASKs about disclosure
    posture before running baseline create, walks customers through
    the three modes, and offers a one-shot .dxkit/policy.json snippet
    for pinning the choice repo-wide.
  • dxkit-action skill — new section explains how to act on a
    blocked finding when the baseline is sanitized / ref-based
    (locator stripped at write time; re-run the analyzer for full
    context or allowlist by fingerprint).
  • README + getting-started.md — call out the public-repo
    posture explicitly so customers don't accidentally commit a
    rich baseline to an open-source repo.

Architectural notes

  • Added stale-allow as a new IdentityKind (Rule 9 + Rule 10
    compliant: identityFor case + producer + fixture row +
    removed from DEFERRED_KINDS once the gather pass landed).
  • The hint formatter (block-time guidance for blocked findings)
    consumes the canonical BaselineEntry discriminated union
    directly — no invented intermediate "BlockingFinding" shape.
    TypeScript exhaustiveness across 6+ switches guarantees new
    finding kinds can't ship without matching cases.
  • dxkit-action skill extended with the typed-category +
    surfaces description; SAST recipe redirects from semgrep's
    // nosemgrep: to dxkit's // dxkit-allow: (single canonical
    suppression surface across all scanners).