@vyuhlabs/dxkit 2.5.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/README.md +102 -0
  3. package/dist/allowlist/categories.d.ts +120 -0
  4. package/dist/allowlist/categories.d.ts.map +1 -0
  5. package/dist/allowlist/categories.js +194 -0
  6. package/dist/allowlist/categories.js.map +1 -0
  7. package/dist/allowlist/cli.d.ts +95 -0
  8. package/dist/allowlist/cli.d.ts.map +1 -0
  9. package/dist/allowlist/cli.js +454 -0
  10. package/dist/allowlist/cli.js.map +1 -0
  11. package/dist/allowlist/diff.d.ts +67 -0
  12. package/dist/allowlist/diff.d.ts.map +1 -0
  13. package/dist/allowlist/diff.js +147 -0
  14. package/dist/allowlist/diff.js.map +1 -0
  15. package/dist/allowlist/file.d.ts +249 -0
  16. package/dist/allowlist/file.d.ts.map +1 -0
  17. package/dist/allowlist/file.js +497 -0
  18. package/dist/allowlist/file.js.map +1 -0
  19. package/dist/allowlist/gather.d.ts +61 -0
  20. package/dist/allowlist/gather.d.ts.map +1 -0
  21. package/dist/allowlist/gather.js +143 -0
  22. package/dist/allowlist/gather.js.map +1 -0
  23. package/dist/allowlist/hint.d.ts +80 -0
  24. package/dist/allowlist/hint.d.ts.map +1 -0
  25. package/dist/allowlist/hint.js +271 -0
  26. package/dist/allowlist/hint.js.map +1 -0
  27. package/dist/allowlist/inline.d.ts +149 -0
  28. package/dist/allowlist/inline.d.ts.map +1 -0
  29. package/dist/allowlist/inline.js +306 -0
  30. package/dist/allowlist/inline.js.map +1 -0
  31. package/dist/baseline/baseline-file.d.ts +7 -0
  32. package/dist/baseline/baseline-file.d.ts.map +1 -1
  33. package/dist/baseline/baseline-file.js +22 -1
  34. package/dist/baseline/baseline-file.js.map +1 -1
  35. package/dist/baseline/check-renderers.d.ts +13 -1
  36. package/dist/baseline/check-renderers.d.ts.map +1 -1
  37. package/dist/baseline/check-renderers.js +67 -1
  38. package/dist/baseline/check-renderers.js.map +1 -1
  39. package/dist/baseline/check.d.ts +33 -7
  40. package/dist/baseline/check.d.ts.map +1 -1
  41. package/dist/baseline/check.js +90 -64
  42. package/dist/baseline/check.js.map +1 -1
  43. package/dist/baseline/create.d.ts +35 -7
  44. package/dist/baseline/create.d.ts.map +1 -1
  45. package/dist/baseline/create.js +43 -5
  46. package/dist/baseline/create.js.map +1 -1
  47. package/dist/baseline/entry-to-located.d.ts +6 -1
  48. package/dist/baseline/entry-to-located.d.ts.map +1 -1
  49. package/dist/baseline/entry-to-located.js +20 -2
  50. package/dist/baseline/entry-to-located.js.map +1 -1
  51. package/dist/baseline/finding-identity.d.ts.map +1 -1
  52. package/dist/baseline/finding-identity.js +15 -13
  53. package/dist/baseline/finding-identity.js.map +1 -1
  54. package/dist/baseline/modes.d.ts +140 -0
  55. package/dist/baseline/modes.d.ts.map +1 -0
  56. package/dist/baseline/modes.js +179 -0
  57. package/dist/baseline/modes.js.map +1 -0
  58. package/dist/baseline/policy.d.ts +64 -0
  59. package/dist/baseline/policy.d.ts.map +1 -1
  60. package/dist/baseline/policy.js +102 -1
  61. package/dist/baseline/policy.js.map +1 -1
  62. package/dist/baseline/producers/health.d.ts +2 -2
  63. package/dist/baseline/producers/health.d.ts.map +1 -1
  64. package/dist/baseline/producers/health.js.map +1 -1
  65. package/dist/baseline/producers/index.d.ts +11 -5
  66. package/dist/baseline/producers/index.d.ts.map +1 -1
  67. package/dist/baseline/producers/index.js +12 -9
  68. package/dist/baseline/producers/index.js.map +1 -1
  69. package/dist/baseline/producers/quality.d.ts +3 -3
  70. package/dist/baseline/producers/quality.d.ts.map +1 -1
  71. package/dist/baseline/producers/quality.js.map +1 -1
  72. package/dist/baseline/producers/secret-hmac.d.ts +2 -2
  73. package/dist/baseline/producers/secret-hmac.d.ts.map +1 -1
  74. package/dist/baseline/producers/secret-hmac.js.map +1 -1
  75. package/dist/baseline/producers/security.d.ts +2 -2
  76. package/dist/baseline/producers/security.d.ts.map +1 -1
  77. package/dist/baseline/producers/security.js.map +1 -1
  78. package/dist/baseline/producers/stale-allow.d.ts +70 -0
  79. package/dist/baseline/producers/stale-allow.d.ts.map +1 -0
  80. package/dist/baseline/producers/stale-allow.js +111 -0
  81. package/dist/baseline/producers/stale-allow.js.map +1 -0
  82. package/dist/baseline/producers/tests.d.ts +2 -2
  83. package/dist/baseline/producers/tests.d.ts.map +1 -1
  84. package/dist/baseline/producers/tests.js.map +1 -1
  85. package/dist/baseline/ref-baseline.d.ts +114 -0
  86. package/dist/baseline/ref-baseline.d.ts.map +1 -0
  87. package/dist/baseline/ref-baseline.js +260 -0
  88. package/dist/baseline/ref-baseline.js.map +1 -0
  89. package/dist/baseline/sanitize.d.ts +80 -0
  90. package/dist/baseline/sanitize.d.ts.map +1 -0
  91. package/dist/baseline/sanitize.js +91 -0
  92. package/dist/baseline/sanitize.js.map +1 -0
  93. package/dist/baseline/show.d.ts.map +1 -1
  94. package/dist/baseline/show.js +9 -3
  95. package/dist/baseline/show.js.map +1 -1
  96. package/dist/baseline/types.d.ts +73 -26
  97. package/dist/baseline/types.d.ts.map +1 -1
  98. package/dist/baseline/types.js +7 -1
  99. package/dist/baseline/types.js.map +1 -1
  100. package/dist/baseline/visibility.d.ts +61 -0
  101. package/dist/baseline/visibility.d.ts.map +1 -0
  102. package/dist/baseline/visibility.js +121 -0
  103. package/dist/baseline/visibility.js.map +1 -0
  104. package/dist/cli.d.ts.map +1 -1
  105. package/dist/cli.js +88 -3
  106. package/dist/cli.js.map +1 -1
  107. package/dist/doctor.d.ts.map +1 -1
  108. package/dist/doctor.js +106 -16
  109. package/dist/doctor.js.map +1 -1
  110. package/dist/issue-cli.d.ts +62 -0
  111. package/dist/issue-cli.d.ts.map +1 -0
  112. package/dist/issue-cli.js +252 -0
  113. package/dist/issue-cli.js.map +1 -0
  114. package/dist/languages/csharp.d.ts.map +1 -1
  115. package/dist/languages/csharp.js +1 -0
  116. package/dist/languages/csharp.js.map +1 -1
  117. package/dist/languages/go.d.ts.map +1 -1
  118. package/dist/languages/go.js +1 -0
  119. package/dist/languages/go.js.map +1 -1
  120. package/dist/languages/java.d.ts.map +1 -1
  121. package/dist/languages/java.js +1 -0
  122. package/dist/languages/java.js.map +1 -1
  123. package/dist/languages/kotlin.d.ts.map +1 -1
  124. package/dist/languages/kotlin.js +1 -0
  125. package/dist/languages/kotlin.js.map +1 -1
  126. package/dist/languages/python.d.ts.map +1 -1
  127. package/dist/languages/python.js +1 -0
  128. package/dist/languages/python.js.map +1 -1
  129. package/dist/languages/ruby.d.ts.map +1 -1
  130. package/dist/languages/ruby.js +1 -0
  131. package/dist/languages/ruby.js.map +1 -1
  132. package/dist/languages/rust.d.ts.map +1 -1
  133. package/dist/languages/rust.js +1 -0
  134. package/dist/languages/rust.js.map +1 -1
  135. package/dist/languages/types.d.ts +25 -0
  136. package/dist/languages/types.d.ts.map +1 -1
  137. package/dist/languages/typescript.d.ts.map +1 -1
  138. package/dist/languages/typescript.js +1 -0
  139. package/dist/languages/typescript.js.map +1 -1
  140. package/package.json +1 -1
  141. package/templates/.claude/skills/dxkit-action/SKILL.md +105 -11
  142. package/templates/.claude/skills/dxkit-onboard/SKILL.md +31 -3
  143. package/dist/baseline/producers/licenses.d.ts +0 -23
  144. package/dist/baseline/producers/licenses.d.ts.map +0 -1
  145. package/dist/baseline/producers/licenses.js +0 -46
  146. package/dist/baseline/producers/licenses.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,170 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.6.0] - 2026-05-23
11
+
12
+ The "per-finding suppression + public-repo-safe baselines" release.
13
+ Adds the typed-category allowlist surface for false-positive /
14
+ test-fixture / mitigated-externally / accepted-risk / deferred
15
+ suppression with inline + file-level modes; retires license
16
+ findings from the baseline (~73% size drop on real customer repos);
17
+ introduces three baseline modes with visibility-aware defaults so
18
+ public repos no longer leak file paths, package names, and
19
+ advisory IDs through a committed baseline.
20
+
21
+ ### Added
22
+
23
+ - **Per-finding allowlist** — `vyuh-dxkit allowlist add/list/show/audit/prune`.
24
+ Typed-category suppression (`false-positive`, `test-fixture`,
25
+ `mitigated-externally`, `accepted-risk`, `deferred`) with required
26
+ reason + (where relevant) expiry. Two surfaces: inline
27
+ `// dxkit-allow:<category> reason="..."` annotations and a
28
+ file-level `.dxkit/allowlist.json`. `accepted-risk` and `deferred`
29
+ require expiry (default 90 days). See
30
+ [docs/commands/allowlist.md](docs/commands/allowlist.md).
31
+ - **Strict stale-annotation detection** — orphaned `dxkit-allow:`
32
+ annotations (where the underlying finding is now gone) emit a
33
+ new `stale-allow` baseline kind on the next scan. The
34
+ TypeScript `@ts-expect-error` pattern, applied to suppressions —
35
+ forces cleanup, prevents the annotation graveyard. Allowlisting
36
+ a `stale-allow` finding is forbidden; only remediation is to
37
+ remove the orphaned comment.
38
+ - **Allowlist activity in PR comments** — the
39
+ `dxkit-guardrails.yml` workflow's sticky PR comment now includes
40
+ an "Allowlist activity" section listing every entry added (or
41
+ removed) on this branch versus the baseline commit. Reviewers
42
+ see new suppressions being introduced and can sanity-check
43
+ category + reason + expiry before approving.
44
+ - **`vyuh-dxkit issue`** — pre-filled GitHub Issues for false
45
+ positives, missing findings, bugs, feature requests, and docs
46
+ gaps. Nothing submits automatically — the CLI opens the
47
+ customer's browser at a new-issue URL with env metadata
48
+ pre-populated, customer reviews + clicks "Submit." See
49
+ [docs/commands/issue.md](docs/commands/issue.md).
50
+ - **`commentSyntax` on language packs** — each pack declares its
51
+ line-comment marker (`#` for python/ruby; `//` for
52
+ typescript/go/rust/csharp/kotlin/java). Drives the inline
53
+ allowlist-annotation generator across every language uniformly.
54
+ Recipe-enforced: scaffolder ships an empty placeholder so
55
+ unfilled packs fail the contract test until populated.
56
+ - Three preemptive architecture rules in `scripts/check-architecture.sh`
57
+ lock down the allowlist canonical entry points: no `createHash`
58
+ inside `src/allowlist/`, no direct `allowlist.json` IO outside
59
+ the canonical loader, no language-comment fallback literals
60
+ (`?? '//'`) anywhere in the module.
61
+
62
+ ### Changed
63
+
64
+ - **License findings retired from the baseline.** Per-package
65
+ license attributions no longer flow through the baseline
66
+ producer registry — they were informational, not regression
67
+ material, and dominated baselines on real customer repos
68
+ (~73% of entries). The canonical license inventory now lives
69
+ solely in `.dxkit/bom.json` (`vyuh-dxkit bom`), which already
70
+ carries richer per-package data (licenseType, licenseText,
71
+ sourceUrl, supplier, releaseDate). Lenient migration:
72
+ baselines written by older dxkit versions still load — the
73
+ reader silently filters retired `license` entries on the way
74
+ in (no file rewrite until the next `baseline create --force`).
75
+ Dependency vulnerability tracking is unchanged — `dep-vuln`
76
+ is a separate identity kind on a separate producer and still
77
+ blocks via the guardrail check.
78
+ - **Sanitization machinery for baseline entries.** New pure
79
+ module `src/baseline/sanitize.ts` introduces a stripped
80
+ `SanitizedBaselineEntry` variant (`{ id, kind, sanitized: true }`)
81
+ carrying identity + kind only. The `sanitizeEntry` /
82
+ `sanitizeFile` pass collapses every rich field; cross-run
83
+ matching still works at full confidence via the fingerprint
84
+ multiset pass. Producers now emit the rich
85
+ `RichBaselineEntry` shape (a `BaselineEntry` excluding the
86
+ sanitized variant); sanitization is a write-time
87
+ transformation, never a producer concern. Consumers walking
88
+ a baseline narrow via the `isSanitized` type guard before
89
+ switching on `entry.kind`. Write-path wiring + visibility-
90
+ aware mode selection ship in a follow-up commit.
91
+
92
+ ### Added
93
+
94
+ - **Three baseline modes with visibility-aware defaults.**
95
+ `committed-full` (today's behavior, rich entries), `committed-
96
+ sanitized` (stripped per-entry payload via the sanitization
97
+ pass), and `ref-based` (no committed file; guardrail check
98
+ recomputes the prior side from a git ref via `git worktree
99
+ add`). The mode is picked by a single resolver
100
+ (`src/baseline/modes.ts`) with precedence: CLI flag →
101
+ `.dxkit/policy.json:baseline.mode` → visibility-derived default
102
+ (public repos auto-pick `ref-based`; everything else picks
103
+ `committed-full`). `committed-sanitized` is never auto-picked
104
+ — it's the explicit opt-in for compliance-conscious private
105
+ repos.
106
+ - `vyuh-dxkit baseline create [--mode <m>] [--ref <r>]` and
107
+ `vyuh-dxkit guardrail check [--mode <m>] [--ref <r>]` — flags
108
+ override `policy.json` for one-off runs.
109
+ - `gh repo view --json visibility` probe + per-process cache
110
+ in `src/baseline/visibility.ts`. Every failure path returns
111
+ `'unknown'`; the resolver treats unknown as private to avoid
112
+ surprise sanitization when `gh auth` lapses.
113
+ - Ref-based gather mechanics in `src/baseline/ref-baseline.ts` —
114
+ `withRefWorktree(opts, fn)` is the reusable primitive; tears
115
+ down the worktree on success + failure. Mirrors file-mode
116
+ `.dxkit/salt` into the worktree so secret-HMAC entries pair
117
+ across cwd + worktree.
118
+
119
+ ### Architectural notes
120
+
121
+ - New CLAUDE.md rule 11: baseline mode resolution flows through
122
+ `resolveBaselineMode`. Two arch-check rules lock the contract:
123
+ no `gh repo view --json visibility` outside
124
+ `src/baseline/visibility.ts`; no `git worktree add` / `remove`
125
+ outside `src/baseline/ref-baseline.ts`.
126
+ - `resolvePolicy` lifted from `check.ts` to `policy.ts` so
127
+ `createBaseline` and `runGuardrailCheck` share one canonical
128
+ loader.
129
+
130
+ ### Discovery surfaces
131
+
132
+ - **PR-comment markdown** now shows the resolved baseline mode in
133
+ the sticky footer (`_Mode_: \`ref-based\` (ref: \`origin/main\`)`).
134
+ Reviewers see WHY a guardrail run picked a given posture.
135
+ - **JSON renderer** carries `baseline.mode = { value, source,
136
+ explanation, ref? }` so agents + dashboards can read the audit
137
+ trail without re-deriving it.
138
+ - **`vyuh-dxkit doctor`** has two new operational checks:
139
+ - "baseline mode: ref-based" / "baseline captured (mode: ...)" —
140
+ the existing baseline-captured check now understands ref-based
141
+ mode (where no on-disk file is expected) so the doctor stops
142
+ reporting a false-negative on public repos.
143
+ - "baseline mode aligned with repo visibility" — warns when an
144
+ explicit `committed-full` pin is in use on a public repo (the
145
+ posture leaks file paths + package names; the auto-picker
146
+ would have chosen ref-based).
147
+ - **`dxkit-onboard` skill** — step 5 now ASKs about disclosure
148
+ posture before running `baseline create`, walks customers through
149
+ the three modes, and offers a one-shot `.dxkit/policy.json` snippet
150
+ for pinning the choice repo-wide.
151
+ - **`dxkit-action` skill** — new section explains how to act on a
152
+ blocked finding when the baseline is sanitized / ref-based
153
+ (locator stripped at write time; re-run the analyzer for full
154
+ context or allowlist by fingerprint).
155
+ - **README + getting-started.md** — call out the public-repo
156
+ posture explicitly so customers don't accidentally commit a
157
+ rich baseline to an open-source repo.
158
+
159
+ ### Architectural notes
160
+
161
+ - Added `stale-allow` as a new `IdentityKind` (Rule 9 + Rule 10
162
+ compliant: identityFor case + producer + fixture row +
163
+ removed from `DEFERRED_KINDS` once the gather pass landed).
164
+ - The hint formatter (block-time guidance for blocked findings)
165
+ consumes the canonical `BaselineEntry` discriminated union
166
+ directly — no invented intermediate "BlockingFinding" shape.
167
+ TypeScript exhaustiveness across 6+ switches guarantees new
168
+ finding kinds can't ship without matching cases.
169
+ - `dxkit-action` skill extended with the typed-category +
170
+ surfaces description; SAST recipe redirects from semgrep's
171
+ `// nosemgrep:` to dxkit's `// dxkit-allow:` (single canonical
172
+ suppression surface across all scanners).
173
+
10
174
  ## [2.5.2] - 2026-05-22
11
175
 
12
176
  The "scaffold UX + lifecycle skills + setup automation" release. Closes
package/README.md CHANGED
@@ -234,6 +234,86 @@ The classifier distinguishes:
234
234
  Customize via [`.dxkit/policy.json`](docs/configuration/policy.md) —
235
235
  auto-discovered when present, compiled-in defaults otherwise.
236
236
 
237
+ ### Baseline modes — public vs private repos
238
+
239
+ The baseline file is committed to git. On public repos that
240
+ disclosure surface matters: a `committed-full` baseline tells anyone
241
+ reading the repo which file/line each finding lives on, which
242
+ private packages you depend on, and which advisory IDs you're
243
+ sitting on unpatched. dxkit ships three modes:
244
+
245
+ | Mode | On-disk content | Auto-default for |
246
+ | --------------------- | -------------------------------------------------------- | ---------------- |
247
+ | `committed-full` | Rich entries (file/line/rule/package/advisory) | private repos |
248
+ | `committed-sanitized` | Stripped to `{ id, kind }` per finding | opt-in |
249
+ | `ref-based` | No file — guardrail recomputes prior side from a git ref | public repos |
250
+
251
+ `vyuh-dxkit baseline create` auto-picks via
252
+ `gh repo view --json visibility`. Pin the choice repo-wide in
253
+ `.dxkit/policy.json`:
254
+
255
+ ```json
256
+ { "baseline": { "mode": "ref-based", "ref": "origin/main" } }
257
+ ```
258
+
259
+ The cross-run matching contract (fingerprint identity) is identical
260
+ across all three modes — sanitization only strips human-readable
261
+ locators, it doesn't change which findings pair across runs. See
262
+ [docs/commands/baseline.md](docs/commands/baseline.md#modes) for the
263
+ full trade-off discussion.
264
+
265
+ ---
266
+
267
+ ## Allowlist: per-finding suppression
268
+
269
+ The baseline handles codebase-wide brownfield acceptance ("today's
270
+ mess is grandfathered; tomorrow's must be net-new improvement").
271
+ For per-finding decisions — false positives, intentional test
272
+ fixtures, externally-mitigated risks, deliberately deferred work —
273
+ use the [allowlist](docs/commands/allowlist.md).
274
+
275
+ Five typed categories signal **why** a finding is suppressed:
276
+
277
+ | Category | Meaning | Expiry |
278
+ | ---------------------- | ------------------------------------------------- | ------------------------------ |
279
+ | `false-positive` | Scanner is wrong about this finding | Optional |
280
+ | `test-fixture` | Intentional pattern in fixture / test code | Optional |
281
+ | `mitigated-externally` | Real risk neutralized at runtime (WAF, env, etc.) | Optional |
282
+ | `accepted-risk` | Real risk, team accepts, signed off | **Required** (default 90 days) |
283
+ | `deferred` | Real, will fix later, tracked work | **Required** |
284
+
285
+ Two surfaces. Inline annotation for source-anchored findings:
286
+
287
+ ```python
288
+ api_key = "sk_test_xxxx" # dxkit-allow:test-fixture reason="placeholder in unit test"
289
+ ```
290
+
291
+ File-level (`.dxkit/allowlist.json`) for cross-file findings or any
292
+ suppression that needs an expiry. New entries appear in the PR-
293
+ comment automation so reviewers see suppressions being introduced
294
+ and can sanity-check the rationale before approving. `audit` /
295
+ `prune` subcommands handle stale + soon-to-expire entries.
296
+
297
+ **Strict cleanup**: orphaned `dxkit-allow:` annotations become
298
+ `stale-allow` findings on the next scan. dxkit refuses to allowlist
299
+ those — the only remediation is to remove the annotation. This is
300
+ the TypeScript `@ts-expect-error` pattern: tools that surface their
301
+ own stale suppressions force cleanup, preventing the annotation
302
+ graveyard.
303
+
304
+ ```bash
305
+ # The block message from `guardrail check` prints the exact command
306
+ # to paste for any blocked finding — file:line for inline-compatible
307
+ # kinds, --fingerprint for everything else.
308
+ vyuh-dxkit allowlist add src/auth/oauth.ts:42 \
309
+ --category=test-fixture --reason="placeholder in unit test"
310
+
311
+ vyuh-dxkit allowlist audit # find stale + soon-to-expire entries
312
+ ```
313
+
314
+ See [`vyuh-dxkit allowlist`](docs/commands/allowlist.md) for the
315
+ full surface.
316
+
237
317
  ---
238
318
 
239
319
  ## Git-aware identity matching
@@ -459,6 +539,28 @@ dxkit is local-first.
459
539
 
460
540
  ---
461
541
 
542
+ ## Reporting issues
543
+
544
+ If a scanner is too noisy, a finding is missing, dxkit itself is
545
+ broken, or the docs are unclear, the fastest path to the dxkit team
546
+ is the built-in issue subcommand:
547
+
548
+ ```bash
549
+ vyuh-dxkit issue --type=false-positive \
550
+ --fingerprint=<id> \
551
+ --about="the scanner flags my intentional X as a Y"
552
+
553
+ vyuh-dxkit issue --type=bug --about="vyuh-dxkit doctor crashes on macOS arm64"
554
+
555
+ vyuh-dxkit issue --type=feature-request --about="add SARIF export"
556
+ ```
557
+
558
+ It opens a pre-filled [GitHub issue](https://github.com/vyuh-labs/dxkit/issues)
559
+ in your browser with dxkit version + platform info already populated.
560
+ Nothing is submitted until you click "Submit" — you review the
561
+ prefill first. See [`vyuh-dxkit issue`](docs/commands/issue.md) for
562
+ the full reference.
563
+
462
564
  ## Contributing
463
565
 
464
566
  dxkit aims to be the standard agentic-development layer for any
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Allowlist category taxonomy. Single source of truth for:
3
+ * - Which categories exist
4
+ * - Which categories require an expiry date
5
+ * - Which categories may be expressed via inline source annotation
6
+ * - Which categories apply to each `IdentityKind`
7
+ * - Which finding kinds support inline annotations at all
8
+ *
9
+ * Pure module — no I/O, no analyzer dependencies. Consumed by the
10
+ * allowlist file reader/writer, the inline-annotation parser, the
11
+ * CLI, the block-time hint formatter, and the new `allowlistHits`
12
+ * baseline producer.
13
+ *
14
+ * See tmp/2.6-allowlist-design.md for the design discussion.
15
+ */
16
+ import type { IdentityKind } from '../baseline/producers';
17
+ /**
18
+ * Single source of truth for category values. The `AllowlistCategory`
19
+ * union type is derived from this array via `(typeof ...)[number]`,
20
+ * so adding a new category means appending one string here and every
21
+ * type-level check (Record-keyed tables, switch exhaustiveness,
22
+ * function parameter types) auto-updates. No two-place drift.
23
+ */
24
+ export declare const ALL_CATEGORIES: readonly ["false-positive", "test-fixture", "mitigated-externally", "accepted-risk", "deferred"];
25
+ export type AllowlistCategory = (typeof ALL_CATEGORIES)[number];
26
+ /**
27
+ * Categories that REQUIRE a finite expiry date. The file-level
28
+ * allowlist write-path rejects entries in these categories without
29
+ * an `expiresAt`. The CLI defaults `expiresAt` to 90 days out for
30
+ * these — see `defaultExpiryDate`.
31
+ *
32
+ * Categories OUTSIDE this set represent stable assertions about the
33
+ * code that don't naturally stale (a test fixture remains a test
34
+ * fixture; a false positive remains a false positive until the
35
+ * scanner rule changes). They may carry an `expiresAt` if the
36
+ * customer chooses, but it's not enforced.
37
+ */
38
+ export declare const EXPIRING_CATEGORIES: ReadonlySet<AllowlistCategory>;
39
+ /**
40
+ * Categories that may be expressed via inline source annotation
41
+ * (`// dxkit-allow:<category> reason="..."`). The complement
42
+ * (`accepted-risk`, `deferred`) is file-only because those categories
43
+ * need fields (expiresAt, acknowledgedSeverity) that don't fit
44
+ * cleanly into a code comment.
45
+ */
46
+ export declare const INLINE_COMPATIBLE_CATEGORIES: ReadonlySet<AllowlistCategory>;
47
+ /**
48
+ * Finding kinds that have a stable single-line attachment point and
49
+ * therefore support inline annotations. Kinds outside this set are
50
+ * file-only (whole-file findings, cross-file findings, gap findings).
51
+ *
52
+ * Inline-compatible:
53
+ * - `secret` / `secret-hmac`: the source line is the credential
54
+ * - `code` / `config`: the source line is the flagged pattern
55
+ * - `dep-vuln`: annotate the import or first-use line
56
+ * - `hygiene`: the source line carries the TODO/FIXME/HACK marker
57
+ *
58
+ * File-only (no single-line site):
59
+ * - `duplication`: two locations across files
60
+ * - `coverage-gap` / `test-gap` / `test-file-degradation`: file or
61
+ * symbol-range level, not single-line
62
+ * - `god-file` / `large-file` / `stale-file`: whole-file findings
63
+ */
64
+ export declare const INLINE_COMPATIBLE_KINDS: ReadonlySet<IdentityKind>;
65
+ /**
66
+ * Categories applicable to each `IdentityKind`. Reflects what
67
+ * suppression rationales the kind can plausibly carry — a
68
+ * `coverage-gap` is rarely a "false positive" in the same way a
69
+ * scanner finding is; a `dep-vuln` is rarely a "test fixture."
70
+ *
71
+ * The CLI presents the applicable list as a multiple-choice prompt
72
+ * when the customer runs `vyuh-dxkit allowlist add` against a
73
+ * finding.
74
+ *
75
+ * The `Record<IdentityKind, ...>` ties this table to the canonical
76
+ * union: TypeScript fails the build when a new `IdentityKind`
77
+ * variant lands without a corresponding entry here.
78
+ */
79
+ export declare const CATEGORIES_BY_KIND: Readonly<Record<IdentityKind, readonly AllowlistCategory[]>>;
80
+ /**
81
+ * Whether a (kind, category) tuple may be expressed as an inline
82
+ * annotation. Both the kind AND the category must be inline-compatible.
83
+ *
84
+ * Examples:
85
+ * canUseInline('secret', 'test-fixture') // true
86
+ * canUseInline('secret', 'accepted-risk') // false (category file-only)
87
+ * canUseInline('large-file', 'false-positive') // false (kind file-only)
88
+ * canUseInline('hygiene', 'accepted-risk') // false (category file-only)
89
+ */
90
+ export declare function canUseInline(kind: IdentityKind, category: AllowlistCategory): boolean;
91
+ /**
92
+ * Whether a category requires `expiresAt` on the file-level entry.
93
+ * Source of truth for the write-path validation rule.
94
+ */
95
+ export declare function requiresExpiry(category: AllowlistCategory): boolean;
96
+ /**
97
+ * Whether a (kind, category) tuple is semantically valid. The CLI
98
+ * uses this to reject incoherent combinations like
99
+ * `coverage-gap + false-positive` with a clear error pointing at
100
+ * the applicable categories for that kind.
101
+ */
102
+ export declare function isCategoryValidForKind(kind: IdentityKind, category: AllowlistCategory): boolean;
103
+ /**
104
+ * Number of days into the future the CLI defaults `expiresAt` to
105
+ * when the customer doesn't specify one. Locked at 90 in Sprint 0
106
+ * (Snyk + Dependabot industry default). Per-category overrides will
107
+ * land in `.dxkit/policy.json` (`allowlist.defaultExpiryDays`) in a
108
+ * follow-up commit if real customer signal demands it.
109
+ */
110
+ export declare const DEFAULT_EXPIRY_DAYS = 90;
111
+ /**
112
+ * Compute the default expiry date as an ISO `YYYY-MM-DD` string,
113
+ * `DEFAULT_EXPIRY_DAYS` from `now`. UTC-anchored to keep the date
114
+ * stable across timezone-different developers on the same team.
115
+ *
116
+ * `now` is injected for deterministic testing — production callers
117
+ * pass `new Date()` (the default).
118
+ */
119
+ export declare function defaultExpiryDate(now?: Date): string;
120
+ //# sourceMappingURL=categories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"categories.d.ts","sourceRoot":"","sources":["../../src/allowlist/categories.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,kGAMjB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,iBAAiB,CAG7D,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,iBAAiB,CAItE,CAAC;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,YAAY,CAO5D,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC,CA6C3F,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAErF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAE/F;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAIhE"}
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ /**
3
+ * Allowlist category taxonomy. Single source of truth for:
4
+ * - Which categories exist
5
+ * - Which categories require an expiry date
6
+ * - Which categories may be expressed via inline source annotation
7
+ * - Which categories apply to each `IdentityKind`
8
+ * - Which finding kinds support inline annotations at all
9
+ *
10
+ * Pure module — no I/O, no analyzer dependencies. Consumed by the
11
+ * allowlist file reader/writer, the inline-annotation parser, the
12
+ * CLI, the block-time hint formatter, and the new `allowlistHits`
13
+ * baseline producer.
14
+ *
15
+ * See tmp/2.6-allowlist-design.md for the design discussion.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.DEFAULT_EXPIRY_DAYS = exports.CATEGORIES_BY_KIND = exports.INLINE_COMPATIBLE_KINDS = exports.INLINE_COMPATIBLE_CATEGORIES = exports.EXPIRING_CATEGORIES = exports.ALL_CATEGORIES = void 0;
19
+ exports.canUseInline = canUseInline;
20
+ exports.requiresExpiry = requiresExpiry;
21
+ exports.isCategoryValidForKind = isCategoryValidForKind;
22
+ exports.defaultExpiryDate = defaultExpiryDate;
23
+ /**
24
+ * Single source of truth for category values. The `AllowlistCategory`
25
+ * union type is derived from this array via `(typeof ...)[number]`,
26
+ * so adding a new category means appending one string here and every
27
+ * type-level check (Record-keyed tables, switch exhaustiveness,
28
+ * function parameter types) auto-updates. No two-place drift.
29
+ */
30
+ exports.ALL_CATEGORIES = [
31
+ 'false-positive',
32
+ 'test-fixture',
33
+ 'mitigated-externally',
34
+ 'accepted-risk',
35
+ 'deferred',
36
+ ];
37
+ /**
38
+ * Categories that REQUIRE a finite expiry date. The file-level
39
+ * allowlist write-path rejects entries in these categories without
40
+ * an `expiresAt`. The CLI defaults `expiresAt` to 90 days out for
41
+ * these — see `defaultExpiryDate`.
42
+ *
43
+ * Categories OUTSIDE this set represent stable assertions about the
44
+ * code that don't naturally stale (a test fixture remains a test
45
+ * fixture; a false positive remains a false positive until the
46
+ * scanner rule changes). They may carry an `expiresAt` if the
47
+ * customer chooses, but it's not enforced.
48
+ */
49
+ exports.EXPIRING_CATEGORIES = new Set([
50
+ 'accepted-risk',
51
+ 'deferred',
52
+ ]);
53
+ /**
54
+ * Categories that may be expressed via inline source annotation
55
+ * (`// dxkit-allow:<category> reason="..."`). The complement
56
+ * (`accepted-risk`, `deferred`) is file-only because those categories
57
+ * need fields (expiresAt, acknowledgedSeverity) that don't fit
58
+ * cleanly into a code comment.
59
+ */
60
+ exports.INLINE_COMPATIBLE_CATEGORIES = new Set([
61
+ 'false-positive',
62
+ 'test-fixture',
63
+ 'mitigated-externally',
64
+ ]);
65
+ /**
66
+ * Finding kinds that have a stable single-line attachment point and
67
+ * therefore support inline annotations. Kinds outside this set are
68
+ * file-only (whole-file findings, cross-file findings, gap findings).
69
+ *
70
+ * Inline-compatible:
71
+ * - `secret` / `secret-hmac`: the source line is the credential
72
+ * - `code` / `config`: the source line is the flagged pattern
73
+ * - `dep-vuln`: annotate the import or first-use line
74
+ * - `hygiene`: the source line carries the TODO/FIXME/HACK marker
75
+ *
76
+ * File-only (no single-line site):
77
+ * - `duplication`: two locations across files
78
+ * - `coverage-gap` / `test-gap` / `test-file-degradation`: file or
79
+ * symbol-range level, not single-line
80
+ * - `god-file` / `large-file` / `stale-file`: whole-file findings
81
+ */
82
+ exports.INLINE_COMPATIBLE_KINDS = new Set([
83
+ 'secret',
84
+ 'secret-hmac',
85
+ 'code',
86
+ 'config',
87
+ 'dep-vuln',
88
+ 'hygiene',
89
+ ]);
90
+ /**
91
+ * Categories applicable to each `IdentityKind`. Reflects what
92
+ * suppression rationales the kind can plausibly carry — a
93
+ * `coverage-gap` is rarely a "false positive" in the same way a
94
+ * scanner finding is; a `dep-vuln` is rarely a "test fixture."
95
+ *
96
+ * The CLI presents the applicable list as a multiple-choice prompt
97
+ * when the customer runs `vyuh-dxkit allowlist add` against a
98
+ * finding.
99
+ *
100
+ * The `Record<IdentityKind, ...>` ties this table to the canonical
101
+ * union: TypeScript fails the build when a new `IdentityKind`
102
+ * variant lands without a corresponding entry here.
103
+ */
104
+ exports.CATEGORIES_BY_KIND = {
105
+ // Source-level security findings: every category applies
106
+ secret: ['false-positive', 'test-fixture', 'mitigated-externally', 'accepted-risk', 'deferred'],
107
+ 'secret-hmac': [
108
+ 'false-positive',
109
+ 'test-fixture',
110
+ 'mitigated-externally',
111
+ 'accepted-risk',
112
+ 'deferred',
113
+ ],
114
+ code: ['false-positive', 'test-fixture', 'mitigated-externally', 'accepted-risk', 'deferred'],
115
+ config: ['false-positive', 'test-fixture', 'mitigated-externally', 'accepted-risk', 'deferred'],
116
+ // Dependency vulnerabilities: rarely a test fixture (the dep is real);
117
+ // every other category applies
118
+ 'dep-vuln': ['false-positive', 'mitigated-externally', 'accepted-risk', 'deferred'],
119
+ // Duplicate blocks: occasionally a false positive (jscpd matched
120
+ // generated code); otherwise accepted-risk or deferred
121
+ duplication: ['false-positive', 'accepted-risk', 'deferred'],
122
+ // Coverage / test gaps: not "false-positive" in any practical sense;
123
+ // only accepted-risk or deferred
124
+ 'coverage-gap': ['accepted-risk', 'deferred'],
125
+ 'test-gap': ['accepted-risk', 'deferred'],
126
+ 'test-file-degradation': ['accepted-risk', 'deferred'],
127
+ // Whole-file findings: false-positive (file IS not actually large /
128
+ // stale / god when reviewed); otherwise accepted-risk or deferred
129
+ 'god-file': ['false-positive', 'accepted-risk', 'deferred'],
130
+ 'large-file': ['false-positive', 'accepted-risk', 'deferred'],
131
+ 'stale-file': ['false-positive', 'accepted-risk', 'deferred'],
132
+ // TODO / FIXME / HACK / console-log / any-type markers: only
133
+ // accepted-risk or deferred (the marker IS the hygiene issue)
134
+ hygiene: ['accepted-risk', 'deferred'],
135
+ // Stale-allow (orphaned inline allowlist annotation): never
136
+ // allowlisted. The right response is always "remove the stale
137
+ // annotation" — allowlisting the warning that an annotation is
138
+ // stale would defeat the entire strict-stale-detection model
139
+ // (TypeScript's @ts-expect-error pattern). Empty array means the
140
+ // CLI rejects with a hint pointing at the annotation's source
141
+ // location.
142
+ 'stale-allow': [],
143
+ };
144
+ /**
145
+ * Whether a (kind, category) tuple may be expressed as an inline
146
+ * annotation. Both the kind AND the category must be inline-compatible.
147
+ *
148
+ * Examples:
149
+ * canUseInline('secret', 'test-fixture') // true
150
+ * canUseInline('secret', 'accepted-risk') // false (category file-only)
151
+ * canUseInline('large-file', 'false-positive') // false (kind file-only)
152
+ * canUseInline('hygiene', 'accepted-risk') // false (category file-only)
153
+ */
154
+ function canUseInline(kind, category) {
155
+ return exports.INLINE_COMPATIBLE_KINDS.has(kind) && exports.INLINE_COMPATIBLE_CATEGORIES.has(category);
156
+ }
157
+ /**
158
+ * Whether a category requires `expiresAt` on the file-level entry.
159
+ * Source of truth for the write-path validation rule.
160
+ */
161
+ function requiresExpiry(category) {
162
+ return exports.EXPIRING_CATEGORIES.has(category);
163
+ }
164
+ /**
165
+ * Whether a (kind, category) tuple is semantically valid. The CLI
166
+ * uses this to reject incoherent combinations like
167
+ * `coverage-gap + false-positive` with a clear error pointing at
168
+ * the applicable categories for that kind.
169
+ */
170
+ function isCategoryValidForKind(kind, category) {
171
+ return exports.CATEGORIES_BY_KIND[kind].includes(category);
172
+ }
173
+ /**
174
+ * Number of days into the future the CLI defaults `expiresAt` to
175
+ * when the customer doesn't specify one. Locked at 90 in Sprint 0
176
+ * (Snyk + Dependabot industry default). Per-category overrides will
177
+ * land in `.dxkit/policy.json` (`allowlist.defaultExpiryDays`) in a
178
+ * follow-up commit if real customer signal demands it.
179
+ */
180
+ exports.DEFAULT_EXPIRY_DAYS = 90;
181
+ /**
182
+ * Compute the default expiry date as an ISO `YYYY-MM-DD` string,
183
+ * `DEFAULT_EXPIRY_DAYS` from `now`. UTC-anchored to keep the date
184
+ * stable across timezone-different developers on the same team.
185
+ *
186
+ * `now` is injected for deterministic testing — production callers
187
+ * pass `new Date()` (the default).
188
+ */
189
+ function defaultExpiryDate(now = new Date()) {
190
+ const expires = new Date(now);
191
+ expires.setUTCDate(expires.getUTCDate() + exports.DEFAULT_EXPIRY_DAYS);
192
+ return expires.toISOString().slice(0, 10);
193
+ }
194
+ //# sourceMappingURL=categories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"categories.js","sourceRoot":"","sources":["../../src/allowlist/categories.ts"],"names":[],"mappings":"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"}