@vyuhlabs/dxkit 2.5.1 → 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 (200) hide show
  1. package/CHANGELOG.md +318 -0
  2. package/README.md +150 -28
  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/analyzers/tools/tool-registry.d.ts.map +1 -1
  32. package/dist/analyzers/tools/tool-registry.js +25 -8
  33. package/dist/analyzers/tools/tool-registry.js.map +1 -1
  34. package/dist/baseline/baseline-file.d.ts +7 -0
  35. package/dist/baseline/baseline-file.d.ts.map +1 -1
  36. package/dist/baseline/baseline-file.js +22 -1
  37. package/dist/baseline/baseline-file.js.map +1 -1
  38. package/dist/baseline/check-renderers.d.ts +13 -1
  39. package/dist/baseline/check-renderers.d.ts.map +1 -1
  40. package/dist/baseline/check-renderers.js +67 -1
  41. package/dist/baseline/check-renderers.js.map +1 -1
  42. package/dist/baseline/check.d.ts +33 -7
  43. package/dist/baseline/check.d.ts.map +1 -1
  44. package/dist/baseline/check.js +90 -64
  45. package/dist/baseline/check.js.map +1 -1
  46. package/dist/baseline/create.d.ts +35 -7
  47. package/dist/baseline/create.d.ts.map +1 -1
  48. package/dist/baseline/create.js +43 -5
  49. package/dist/baseline/create.js.map +1 -1
  50. package/dist/baseline/entry-to-located.d.ts +6 -1
  51. package/dist/baseline/entry-to-located.d.ts.map +1 -1
  52. package/dist/baseline/entry-to-located.js +20 -2
  53. package/dist/baseline/entry-to-located.js.map +1 -1
  54. package/dist/baseline/finding-identity.d.ts.map +1 -1
  55. package/dist/baseline/finding-identity.js +15 -13
  56. package/dist/baseline/finding-identity.js.map +1 -1
  57. package/dist/baseline/modes.d.ts +140 -0
  58. package/dist/baseline/modes.d.ts.map +1 -0
  59. package/dist/baseline/modes.js +179 -0
  60. package/dist/baseline/modes.js.map +1 -0
  61. package/dist/baseline/policy.d.ts +64 -0
  62. package/dist/baseline/policy.d.ts.map +1 -1
  63. package/dist/baseline/policy.js +102 -1
  64. package/dist/baseline/policy.js.map +1 -1
  65. package/dist/baseline/producers/health.d.ts +2 -2
  66. package/dist/baseline/producers/health.d.ts.map +1 -1
  67. package/dist/baseline/producers/health.js.map +1 -1
  68. package/dist/baseline/producers/index.d.ts +11 -5
  69. package/dist/baseline/producers/index.d.ts.map +1 -1
  70. package/dist/baseline/producers/index.js +12 -9
  71. package/dist/baseline/producers/index.js.map +1 -1
  72. package/dist/baseline/producers/quality.d.ts +3 -3
  73. package/dist/baseline/producers/quality.d.ts.map +1 -1
  74. package/dist/baseline/producers/quality.js.map +1 -1
  75. package/dist/baseline/producers/secret-hmac.d.ts +2 -2
  76. package/dist/baseline/producers/secret-hmac.d.ts.map +1 -1
  77. package/dist/baseline/producers/secret-hmac.js.map +1 -1
  78. package/dist/baseline/producers/security.d.ts +2 -2
  79. package/dist/baseline/producers/security.d.ts.map +1 -1
  80. package/dist/baseline/producers/security.js.map +1 -1
  81. package/dist/baseline/producers/stale-allow.d.ts +70 -0
  82. package/dist/baseline/producers/stale-allow.d.ts.map +1 -0
  83. package/dist/baseline/producers/stale-allow.js +111 -0
  84. package/dist/baseline/producers/stale-allow.js.map +1 -0
  85. package/dist/baseline/producers/tests.d.ts +2 -2
  86. package/dist/baseline/producers/tests.d.ts.map +1 -1
  87. package/dist/baseline/producers/tests.js.map +1 -1
  88. package/dist/baseline/ref-baseline.d.ts +114 -0
  89. package/dist/baseline/ref-baseline.d.ts.map +1 -0
  90. package/dist/baseline/ref-baseline.js +260 -0
  91. package/dist/baseline/ref-baseline.js.map +1 -0
  92. package/dist/baseline/sanitize.d.ts +80 -0
  93. package/dist/baseline/sanitize.d.ts.map +1 -0
  94. package/dist/baseline/sanitize.js +91 -0
  95. package/dist/baseline/sanitize.js.map +1 -0
  96. package/dist/baseline/show.d.ts.map +1 -1
  97. package/dist/baseline/show.js +9 -3
  98. package/dist/baseline/show.js.map +1 -1
  99. package/dist/baseline/types.d.ts +73 -26
  100. package/dist/baseline/types.d.ts.map +1 -1
  101. package/dist/baseline/types.js +7 -1
  102. package/dist/baseline/types.js.map +1 -1
  103. package/dist/baseline/visibility.d.ts +61 -0
  104. package/dist/baseline/visibility.d.ts.map +1 -0
  105. package/dist/baseline/visibility.js +121 -0
  106. package/dist/baseline/visibility.js.map +1 -0
  107. package/dist/cli.d.ts.map +1 -1
  108. package/dist/cli.js +154 -13
  109. package/dist/cli.js.map +1 -1
  110. package/dist/constants.d.ts.map +1 -1
  111. package/dist/constants.js +0 -10
  112. package/dist/constants.js.map +1 -1
  113. package/dist/detect.d.ts.map +1 -1
  114. package/dist/detect.js +0 -15
  115. package/dist/detect.js.map +1 -1
  116. package/dist/doctor.d.ts +78 -1
  117. package/dist/doctor.d.ts.map +1 -1
  118. package/dist/doctor.js +590 -101
  119. package/dist/doctor.js.map +1 -1
  120. package/dist/generator.d.ts.map +1 -1
  121. package/dist/generator.js +15 -0
  122. package/dist/generator.js.map +1 -1
  123. package/dist/issue-cli.d.ts +62 -0
  124. package/dist/issue-cli.d.ts.map +1 -0
  125. package/dist/issue-cli.js +252 -0
  126. package/dist/issue-cli.js.map +1 -0
  127. package/dist/languages/csharp.d.ts.map +1 -1
  128. package/dist/languages/csharp.js +2 -0
  129. package/dist/languages/csharp.js.map +1 -1
  130. package/dist/languages/go.d.ts.map +1 -1
  131. package/dist/languages/go.js +2 -0
  132. package/dist/languages/go.js.map +1 -1
  133. package/dist/languages/index.d.ts +25 -0
  134. package/dist/languages/index.d.ts.map +1 -1
  135. package/dist/languages/index.js +44 -0
  136. package/dist/languages/index.js.map +1 -1
  137. package/dist/languages/java.d.ts.map +1 -1
  138. package/dist/languages/java.js +2 -0
  139. package/dist/languages/java.js.map +1 -1
  140. package/dist/languages/kotlin.d.ts.map +1 -1
  141. package/dist/languages/kotlin.js +2 -0
  142. package/dist/languages/kotlin.js.map +1 -1
  143. package/dist/languages/python.d.ts.map +1 -1
  144. package/dist/languages/python.js +11 -1
  145. package/dist/languages/python.js.map +1 -1
  146. package/dist/languages/ruby.d.ts.map +1 -1
  147. package/dist/languages/ruby.js +2 -0
  148. package/dist/languages/ruby.js.map +1 -1
  149. package/dist/languages/rust.d.ts.map +1 -1
  150. package/dist/languages/rust.js +2 -0
  151. package/dist/languages/rust.js.map +1 -1
  152. package/dist/languages/types.d.ts +45 -0
  153. package/dist/languages/types.d.ts.map +1 -1
  154. package/dist/languages/typescript.d.ts.map +1 -1
  155. package/dist/languages/typescript.js +2 -0
  156. package/dist/languages/typescript.js.map +1 -1
  157. package/dist/prompts.d.ts.map +1 -1
  158. package/dist/prompts.js +0 -5
  159. package/dist/prompts.js.map +1 -1
  160. package/dist/setup-branch-protection.d.ts +34 -0
  161. package/dist/setup-branch-protection.d.ts.map +1 -0
  162. package/dist/setup-branch-protection.js +190 -0
  163. package/dist/setup-branch-protection.js.map +1 -0
  164. package/dist/setup-gh.d.ts +75 -0
  165. package/dist/setup-gh.d.ts.map +1 -0
  166. package/dist/setup-gh.js +213 -0
  167. package/dist/setup-gh.js.map +1 -0
  168. package/dist/setup-prebuild.d.ts +34 -0
  169. package/dist/setup-prebuild.d.ts.map +1 -0
  170. package/dist/setup-prebuild.js +181 -0
  171. package/dist/setup-prebuild.js.map +1 -0
  172. package/dist/ship-installers.d.ts.map +1 -1
  173. package/dist/ship-installers.js +19 -4
  174. package/dist/ship-installers.js.map +1 -1
  175. package/dist/types.d.ts +24 -6
  176. package/dist/types.d.ts.map +1 -1
  177. package/dist/update.d.ts +41 -0
  178. package/dist/update.d.ts.map +1 -1
  179. package/dist/update.js +154 -15
  180. package/dist/update.js.map +1 -1
  181. package/dist/upgrade.d.ts +88 -0
  182. package/dist/upgrade.d.ts.map +1 -0
  183. package/dist/upgrade.js +324 -0
  184. package/dist/upgrade.js.map +1 -0
  185. package/package.json +1 -1
  186. package/templates/.claude/skills/dxkit-action/SKILL.md +111 -17
  187. package/templates/.claude/skills/dxkit-config/SKILL.md +7 -7
  188. package/templates/.claude/skills/dxkit-fix/SKILL.md +165 -0
  189. package/templates/.claude/skills/dxkit-hooks/SKILL.md +8 -8
  190. package/templates/.claude/skills/dxkit-init/SKILL.md +3 -3
  191. package/templates/.claude/skills/dxkit-learn/SKILL.md +9 -9
  192. package/templates/.claude/skills/dxkit-onboard/SKILL.md +274 -0
  193. package/templates/.claude/skills/dxkit-reports/SKILL.md +18 -18
  194. package/templates/.claude/skills/dxkit-update/SKILL.md +164 -0
  195. package/templates/.devcontainer/devcontainer.json +6 -15
  196. package/templates/.devcontainer/post-create.sh +19 -4
  197. package/dist/baseline/producers/licenses.d.ts +0 -23
  198. package/dist/baseline/producers/licenses.d.ts.map +0 -1
  199. package/dist/baseline/producers/licenses.js +0 -46
  200. package/dist/baseline/producers/licenses.js.map +0 -1
@@ -0,0 +1,497 @@
1
+ "use strict";
2
+ /**
3
+ * On-disk allowlist file — `.dxkit/allowlist.json` + optional sidecar
4
+ * `.dxkit/allowlist-reasons.local.json`.
5
+ *
6
+ * The allowlist file is the durable contract for per-finding
7
+ * suppressions: customer has reviewed this fingerprint, categorized
8
+ * the reason, and accepts that the guardrail should let the finding
9
+ * pass on future runs.
10
+ *
11
+ * Identity is the 16-char hex fingerprint from
12
+ * `src/analyzers/tools/fingerprint.ts` (CLAUDE.md Rule 9). An entry
13
+ * matches a finding when their fingerprint strings are byte-equal.
14
+ *
15
+ * # Two modes, one schema
16
+ *
17
+ * The mode is recorded in `.dxkit/policy.json` (out of scope for this
18
+ * module — consumers pass it in via `AllowlistMode`). Both modes use
19
+ * the same `AllowlistFile` shape on disk; sanitized mode just drops
20
+ * the human-readable fields and pushes them to a sidecar:
21
+ *
22
+ * `'full'` — every field present on the entry. Default for
23
+ * private repos. The committed file carries the full audit trail.
24
+ *
25
+ * `'sanitized'` — entries carry `fingerprint + kind + category +
26
+ * addedAt + expiresAt + acknowledgedSeverity` only. The
27
+ * `reason` + `addedBy` fields live in the gitignored sidecar
28
+ * `.dxkit/allowlist-reasons.local.json`, keyed by fingerprint.
29
+ * Default for public repos. Loaders merge the sidecar back when
30
+ * present; readers tolerate its absence (no reason field, but
31
+ * the suppression still applies because matching is by
32
+ * fingerprint).
33
+ *
34
+ * # Validation surface
35
+ *
36
+ * `validateAllowlistFile` enforces every Sprint-0-locked rule:
37
+ * - `reason` is required (full mode) / required in sidecar
38
+ * (sanitized mode)
39
+ * - `category` is one of the five canonical values
40
+ * - `category` is valid for the entry's `kind`
41
+ * - `requiresExpiry(category)` ⇒ `expiresAt` is present + parseable
42
+ * - `acknowledgedSeverity` is required when `category` is
43
+ * `accepted-risk` AND `severity` is `high`/`critical`
44
+ */
45
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
46
+ if (k2 === undefined) k2 = k;
47
+ var desc = Object.getOwnPropertyDescriptor(m, k);
48
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
49
+ desc = { enumerable: true, get: function() { return m[k]; } };
50
+ }
51
+ Object.defineProperty(o, k2, desc);
52
+ }) : (function(o, m, k, k2) {
53
+ if (k2 === undefined) k2 = k;
54
+ o[k2] = m[k];
55
+ }));
56
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
57
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
58
+ }) : function(o, v) {
59
+ o["default"] = v;
60
+ });
61
+ var __importStar = (this && this.__importStar) || (function () {
62
+ var ownKeys = function(o) {
63
+ ownKeys = Object.getOwnPropertyNames || function (o) {
64
+ var ar = [];
65
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
66
+ return ar;
67
+ };
68
+ return ownKeys(o);
69
+ };
70
+ return function (mod) {
71
+ if (mod && mod.__esModule) return mod;
72
+ var result = {};
73
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
74
+ __setModuleDefault(result, mod);
75
+ return result;
76
+ };
77
+ })();
78
+ Object.defineProperty(exports, "__esModule", { value: true });
79
+ exports.ALL_MODES = exports.ALLOWLIST_REASONS_FILENAME = exports.ALLOWLIST_FILENAME = exports.ALLOWLIST_DIR = exports.ALLOWLIST_REASONS_SCHEMA_VERSION = exports.ALLOWLIST_SCHEMA_VERSION = void 0;
80
+ exports.pathForAllowlist = pathForAllowlist;
81
+ exports.pathForAllowlistReasons = pathForAllowlistReasons;
82
+ exports.emptyAllowlistFile = emptyAllowlistFile;
83
+ exports.loadAllowlist = loadAllowlist;
84
+ exports.loadAllowlistReasons = loadAllowlistReasons;
85
+ exports.saveAllowlist = saveAllowlist;
86
+ exports.findEntry = findEntry;
87
+ exports.addEntry = addEntry;
88
+ exports.removeEntry = removeEntry;
89
+ exports.matchesFinding = matchesFinding;
90
+ exports.isEntryActive = isEntryActive;
91
+ exports.daysUntilExpiry = daysUntilExpiry;
92
+ exports.auditAllowlist = auditAllowlist;
93
+ exports.pruneExpired = pruneExpired;
94
+ exports.validateAllowlistFile = validateAllowlistFile;
95
+ exports.validateAllowlistEntry = validateAllowlistEntry;
96
+ const fs = __importStar(require("fs"));
97
+ const path = __importStar(require("path"));
98
+ const categories_1 = require("./categories");
99
+ exports.ALLOWLIST_SCHEMA_VERSION = 'dxkit-allowlist/v1';
100
+ exports.ALLOWLIST_REASONS_SCHEMA_VERSION = 'dxkit-allowlist-reasons/v1';
101
+ exports.ALLOWLIST_DIR = '.dxkit';
102
+ exports.ALLOWLIST_FILENAME = 'allowlist.json';
103
+ exports.ALLOWLIST_REASONS_FILENAME = 'allowlist-reasons.local.json';
104
+ /**
105
+ * Single source of truth for mode values. The `AllowlistMode` union
106
+ * is derived from this array via `(typeof ...)[number]`, so adding
107
+ * a new mode means appending one string here — the runtime checks
108
+ * below pick it up via `ALL_MODES.includes(...)` without any
109
+ * literal-value drift between type and runtime.
110
+ */
111
+ exports.ALL_MODES = ['full', 'sanitized'];
112
+ function pathForAllowlist(cwd) {
113
+ return path.join(cwd, exports.ALLOWLIST_DIR, exports.ALLOWLIST_FILENAME);
114
+ }
115
+ function pathForAllowlistReasons(cwd) {
116
+ return path.join(cwd, exports.ALLOWLIST_DIR, exports.ALLOWLIST_REASONS_FILENAME);
117
+ }
118
+ /**
119
+ * Create an empty allowlist file in the requested mode. Useful for
120
+ * the `vyuh-dxkit allowlist add` CLI path when no file exists yet.
121
+ */
122
+ function emptyAllowlistFile(mode = 'full') {
123
+ return { schemaVersion: exports.ALLOWLIST_SCHEMA_VERSION, mode, entries: [] };
124
+ }
125
+ /**
126
+ * Read the on-disk allowlist + (when present) merge the sidecar
127
+ * reasons. Returns `null` when the main file doesn't exist —
128
+ * callers treat that as "no allowlist configured."
129
+ *
130
+ * Throws on:
131
+ * - Malformed JSON in either file
132
+ * - Unrecognized `schemaVersion`
133
+ * - Root is not an object
134
+ *
135
+ * The sidecar's absence is NOT an error: customers in sanitized
136
+ * mode may have a committed allowlist on disk without the sidecar
137
+ * cloned (CI checkout, fresh teammate clone). The fingerprint-only
138
+ * file is still functional as a suppression list.
139
+ */
140
+ function loadAllowlist(cwd) {
141
+ const filePath = pathForAllowlist(cwd);
142
+ if (!fs.existsSync(filePath))
143
+ return null;
144
+ const raw = fs.readFileSync(filePath, 'utf8');
145
+ let parsed;
146
+ try {
147
+ parsed = JSON.parse(raw);
148
+ }
149
+ catch (err) {
150
+ throw new Error(`allowlist file is not valid JSON: ${filePath} (${err.message})`);
151
+ }
152
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
153
+ throw new Error(`allowlist file root is not an object: ${filePath}`);
154
+ }
155
+ const obj = parsed;
156
+ if (obj.schemaVersion !== exports.ALLOWLIST_SCHEMA_VERSION) {
157
+ throw new Error(`allowlist file schemaVersion is ${JSON.stringify(obj.schemaVersion)}; ` +
158
+ `this dxkit understands ${JSON.stringify(exports.ALLOWLIST_SCHEMA_VERSION)} only ` +
159
+ `(${filePath})`);
160
+ }
161
+ if (!isAllowlistMode(obj.mode)) {
162
+ throw new Error(`allowlist file mode is ${JSON.stringify(obj.mode)}; ` +
163
+ `expected one of ${JSON.stringify(exports.ALL_MODES)} (${filePath})`);
164
+ }
165
+ if (!Array.isArray(obj.entries)) {
166
+ throw new Error(`allowlist file entries is not an array (${filePath})`);
167
+ }
168
+ const base = parsed;
169
+ if (base.mode === 'full')
170
+ return base;
171
+ // sanitized mode → merge sidecar if present
172
+ const sidecar = loadAllowlistReasons(cwd);
173
+ if (!sidecar)
174
+ return base;
175
+ return mergeReasons(base, sidecar);
176
+ }
177
+ /**
178
+ * Load the gitignored reasons sidecar. Returns `null` when missing.
179
+ * Throws on malformed JSON or wrong schemaVersion.
180
+ */
181
+ function loadAllowlistReasons(cwd) {
182
+ const filePath = pathForAllowlistReasons(cwd);
183
+ if (!fs.existsSync(filePath))
184
+ return null;
185
+ const raw = fs.readFileSync(filePath, 'utf8');
186
+ let parsed;
187
+ try {
188
+ parsed = JSON.parse(raw);
189
+ }
190
+ catch (err) {
191
+ throw new Error(`allowlist reasons sidecar is not valid JSON: ${filePath} (${err.message})`);
192
+ }
193
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
194
+ throw new Error(`allowlist reasons sidecar root is not an object: ${filePath}`);
195
+ }
196
+ const obj = parsed;
197
+ if (obj.schemaVersion !== exports.ALLOWLIST_REASONS_SCHEMA_VERSION) {
198
+ throw new Error(`allowlist reasons sidecar schemaVersion is ${JSON.stringify(obj.schemaVersion)}; ` +
199
+ `expected ${JSON.stringify(exports.ALLOWLIST_REASONS_SCHEMA_VERSION)} (${filePath})`);
200
+ }
201
+ if (!obj.reasons || typeof obj.reasons !== 'object' || Array.isArray(obj.reasons)) {
202
+ throw new Error(`allowlist reasons sidecar 'reasons' is not an object (${filePath})`);
203
+ }
204
+ return parsed;
205
+ }
206
+ /**
207
+ * Persist the allowlist to disk. Writes the sidecar separately in
208
+ * sanitized mode: the committed file gets the structural fields;
209
+ * the sidecar gets `reason` + `addedBy`.
210
+ *
211
+ * Validation runs before writing — invalid input throws and the
212
+ * file isn't touched. Use `validateAllowlistFile` directly when
213
+ * you want non-throwing error reporting.
214
+ */
215
+ function saveAllowlist(cwd, file) {
216
+ const errors = validateAllowlistFile(file);
217
+ if (errors.length > 0) {
218
+ throw new Error(`allowlist file failed validation:\n` +
219
+ errors.map((e) => ` - ${e.field}: ${e.message}`).join('\n'));
220
+ }
221
+ if (file.mode === 'full') {
222
+ writeJsonPretty(pathForAllowlist(cwd), file);
223
+ return;
224
+ }
225
+ // sanitized mode: split entries → main + sidecar
226
+ const sanitizedEntries = [];
227
+ const reasons = {};
228
+ for (const entry of file.entries) {
229
+ sanitizedEntries.push(sanitizedEntry(entry));
230
+ if (entry.reason !== undefined && entry.addedBy !== undefined) {
231
+ reasons[entry.fingerprint] = { reason: entry.reason, addedBy: entry.addedBy };
232
+ }
233
+ }
234
+ const sanitizedFile = {
235
+ schemaVersion: file.schemaVersion,
236
+ mode: 'sanitized',
237
+ entries: sanitizedEntries,
238
+ };
239
+ const sidecar = {
240
+ schemaVersion: exports.ALLOWLIST_REASONS_SCHEMA_VERSION,
241
+ reasons,
242
+ };
243
+ writeJsonPretty(pathForAllowlist(cwd), sanitizedFile);
244
+ writeJsonPretty(pathForAllowlistReasons(cwd), sidecar);
245
+ }
246
+ /** Find an entry by fingerprint. */
247
+ function findEntry(file, fingerprint) {
248
+ return file.entries.find((e) => e.fingerprint === fingerprint);
249
+ }
250
+ /**
251
+ * Add an entry. Returns a NEW `AllowlistFile` (immutable update).
252
+ * Throws if `entry.fingerprint` already exists.
253
+ */
254
+ function addEntry(file, entry) {
255
+ if (findEntry(file, entry.fingerprint)) {
256
+ throw new Error(`allowlist already contains entry for fingerprint ${entry.fingerprint}; ` +
257
+ `use removeEntry first or update in place`);
258
+ }
259
+ return { ...file, entries: [...file.entries, entry] };
260
+ }
261
+ /**
262
+ * Remove an entry by fingerprint. Returns a NEW `AllowlistFile`.
263
+ * Silently no-ops when the fingerprint isn't present (CLI surfaces
264
+ * the "not found" case through a separate read step).
265
+ */
266
+ function removeEntry(file, fingerprint) {
267
+ return { ...file, entries: file.entries.filter((e) => e.fingerprint !== fingerprint) };
268
+ }
269
+ /**
270
+ * Whether the allowlist suppresses a given finding. Pure
271
+ * fingerprint match. Expiry handling is layered on top by
272
+ * `isEntryActive` (kept separate so the guardrail can distinguish
273
+ * "matched but expired" from "no match").
274
+ */
275
+ function matchesFinding(file, fingerprint) {
276
+ return findEntry(file, fingerprint) !== undefined;
277
+ }
278
+ /**
279
+ * Whether an entry is currently active (within its expiry window).
280
+ * Entries without `expiresAt` are always active. Entries past their
281
+ * expiry are inactive — guardrail treats the underlying finding as
282
+ * un-allowlisted and the next run flags the suppression as stale.
283
+ */
284
+ function isEntryActive(entry, now = new Date()) {
285
+ if (!entry.expiresAt)
286
+ return true;
287
+ const today = now.toISOString().slice(0, 10);
288
+ return entry.expiresAt >= today;
289
+ }
290
+ /**
291
+ * Days remaining until an entry expires, or `null` when it has no
292
+ * expiry. Negative values mean already expired by that many days.
293
+ */
294
+ function daysUntilExpiry(entry, now = new Date()) {
295
+ if (!entry.expiresAt)
296
+ return null;
297
+ const expiry = new Date(entry.expiresAt + 'T00:00:00Z');
298
+ const today = new Date(now.toISOString().slice(0, 10) + 'T00:00:00Z');
299
+ const msPerDay = 24 * 60 * 60 * 1000;
300
+ return Math.round((expiry.getTime() - today.getTime()) / msPerDay);
301
+ }
302
+ function auditAllowlist(file, options = {}) {
303
+ const now = options.now ?? new Date();
304
+ const horizon = options.soonToExpireDays ?? 14;
305
+ const expired = [];
306
+ const soonToExpire = [];
307
+ const missingRationale = [];
308
+ for (const entry of file.entries) {
309
+ const days = daysUntilExpiry(entry, now);
310
+ if (days !== null && days < 0) {
311
+ expired.push(entry);
312
+ }
313
+ else if (days !== null && days <= horizon) {
314
+ soonToExpire.push({ entry, daysRemaining: days });
315
+ }
316
+ if (!entry.reason || entry.reason.trim().length === 0) {
317
+ // In sanitized mode the reason may legitimately live in the
318
+ // gitignored sidecar; flag here so the caller can decide
319
+ // whether to treat it as a real audit item or just an
320
+ // "unavailable locally" notice.
321
+ missingRationale.push(entry);
322
+ }
323
+ }
324
+ return { expired, soonToExpire, missingRationale };
325
+ }
326
+ /**
327
+ * Remove expired entries from the file. Returns a new file (immutable)
328
+ * plus the list of removed entries so the CLI can render what changed.
329
+ * Pure function — no I/O.
330
+ */
331
+ function pruneExpired(file, now = new Date()) {
332
+ const removed = [];
333
+ const keptEntries = [];
334
+ for (const entry of file.entries) {
335
+ if (isEntryActive(entry, now)) {
336
+ keptEntries.push(entry);
337
+ }
338
+ else {
339
+ removed.push(entry);
340
+ }
341
+ }
342
+ return { kept: { ...file, entries: keptEntries }, removed };
343
+ }
344
+ /**
345
+ * Validate every entry in the file against the canonical taxonomy
346
+ * + Sprint-0-locked rules. Pure function; returns an array of
347
+ * `ValidationError` rather than throwing so callers can render
348
+ * structured error messages.
349
+ */
350
+ function validateAllowlistFile(file) {
351
+ const errors = [];
352
+ if (file.schemaVersion !== exports.ALLOWLIST_SCHEMA_VERSION) {
353
+ errors.push({
354
+ field: 'schemaVersion',
355
+ message: `expected ${JSON.stringify(exports.ALLOWLIST_SCHEMA_VERSION)}, got ${JSON.stringify(file.schemaVersion)}`,
356
+ });
357
+ }
358
+ if (!isAllowlistMode(file.mode)) {
359
+ errors.push({
360
+ field: 'mode',
361
+ message: `expected one of ${JSON.stringify(exports.ALL_MODES)}, got ${JSON.stringify(file.mode)}`,
362
+ });
363
+ }
364
+ const seen = new Set();
365
+ for (const entry of file.entries) {
366
+ if (seen.has(entry.fingerprint)) {
367
+ errors.push({
368
+ fingerprint: entry.fingerprint,
369
+ field: 'fingerprint',
370
+ message: `duplicate fingerprint`,
371
+ });
372
+ }
373
+ seen.add(entry.fingerprint);
374
+ for (const err of validateAllowlistEntry(entry, file.mode))
375
+ errors.push(err);
376
+ }
377
+ return errors;
378
+ }
379
+ /**
380
+ * Validate a single entry. Exposed independently so the CLI's
381
+ * `allowlist add` can pre-flight before mutating the file.
382
+ */
383
+ function validateAllowlistEntry(entry, mode) {
384
+ const errors = [];
385
+ const fp = entry.fingerprint;
386
+ if (!fp || typeof fp !== 'string' || !/^[0-9a-f]{16}$/.test(fp)) {
387
+ errors.push({
388
+ fingerprint: fp,
389
+ field: 'fingerprint',
390
+ message: 'must be a 16-char lowercase hex string',
391
+ });
392
+ }
393
+ if (!categories_1.ALL_CATEGORIES.includes(entry.category)) {
394
+ errors.push({
395
+ fingerprint: fp,
396
+ field: 'category',
397
+ message: `must be one of ${JSON.stringify(categories_1.ALL_CATEGORIES)}; got ${JSON.stringify(entry.category)}`,
398
+ });
399
+ }
400
+ if (!(0, categories_1.isCategoryValidForKind)(entry.kind, entry.category)) {
401
+ errors.push({
402
+ fingerprint: fp,
403
+ field: 'category',
404
+ message: `category ${JSON.stringify(entry.category)} does not apply to kind ${JSON.stringify(entry.kind)}`,
405
+ });
406
+ }
407
+ // reason: required in full mode; sidecar-owned in sanitized mode (so
408
+ // the entry on disk legitimately omits it). The CLI write path emits
409
+ // a separate validation pass for the sidecar.
410
+ if (mode === 'full') {
411
+ if (entry.reason === undefined || entry.reason === null) {
412
+ errors.push({ fingerprint: fp, field: 'reason', message: 'required in full mode' });
413
+ }
414
+ else if (typeof entry.reason !== 'string' || entry.reason.trim().length === 0) {
415
+ errors.push({
416
+ fingerprint: fp,
417
+ field: 'reason',
418
+ message: 'must be a non-empty string',
419
+ });
420
+ }
421
+ if (entry.addedBy === undefined || entry.addedBy === null) {
422
+ errors.push({ fingerprint: fp, field: 'addedBy', message: 'required in full mode' });
423
+ }
424
+ else if (typeof entry.addedBy !== 'string' || entry.addedBy.trim().length === 0) {
425
+ errors.push({
426
+ fingerprint: fp,
427
+ field: 'addedBy',
428
+ message: 'must be a non-empty string',
429
+ });
430
+ }
431
+ }
432
+ if (!entry.addedAt || !/^\d{4}-\d{2}-\d{2}$/.test(entry.addedAt)) {
433
+ errors.push({
434
+ fingerprint: fp,
435
+ field: 'addedAt',
436
+ message: 'must be ISO date YYYY-MM-DD',
437
+ });
438
+ }
439
+ if ((0, categories_1.requiresExpiry)(entry.category)) {
440
+ if (!entry.expiresAt) {
441
+ errors.push({
442
+ fingerprint: fp,
443
+ field: 'expiresAt',
444
+ message: `required for category ${JSON.stringify(entry.category)}`,
445
+ });
446
+ }
447
+ }
448
+ if (entry.expiresAt !== undefined && !/^\d{4}-\d{2}-\d{2}$/.test(entry.expiresAt)) {
449
+ errors.push({
450
+ fingerprint: fp,
451
+ field: 'expiresAt',
452
+ message: 'must be ISO date YYYY-MM-DD when present',
453
+ });
454
+ }
455
+ // Note on acknowledgedSeverity: the rule "accepted-risk on a
456
+ // high/critical finding requires acknowledgedSeverity" can't be
457
+ // enforced from inside this validator — the finding's severity
458
+ // doesn't live on the on-disk entry. The CLI's `allowlist add`
459
+ // path enforces it at write time when the finding is in scope.
460
+ return errors;
461
+ }
462
+ // ─── Internals ───────────────────────────────────────────────────────────
463
+ function isAllowlistMode(value) {
464
+ return typeof value === 'string' && exports.ALL_MODES.includes(value);
465
+ }
466
+ function writeJsonPretty(filePath, value) {
467
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
468
+ fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n', 'utf8');
469
+ }
470
+ function sanitizedEntry(entry) {
471
+ // Strip `reason` + `addedBy`; everything else stays on disk in the
472
+ // committed file. The fingerprint contract preserves matching.
473
+ // We intentionally rebuild via property assignment rather than
474
+ // destructuring so the optional-undefined fields don't survive
475
+ // serialization as explicit `null`s.
476
+ const out = {
477
+ fingerprint: entry.fingerprint,
478
+ kind: entry.kind,
479
+ category: entry.category,
480
+ addedAt: entry.addedAt,
481
+ ...(entry.expiresAt !== undefined ? { expiresAt: entry.expiresAt } : {}),
482
+ ...(entry.acknowledgedSeverity !== undefined
483
+ ? { acknowledgedSeverity: entry.acknowledgedSeverity }
484
+ : {}),
485
+ };
486
+ return out;
487
+ }
488
+ function mergeReasons(file, sidecar) {
489
+ const merged = file.entries.map((entry) => {
490
+ const r = sidecar.reasons[entry.fingerprint];
491
+ if (!r)
492
+ return entry;
493
+ return { ...entry, reason: r.reason, addedBy: r.addedBy };
494
+ });
495
+ return { ...file, entries: merged };
496
+ }
497
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/allowlist/file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Inline allowlist annotation gather pass.
3
+ *
4
+ * Walks the source tree looking for `<lineComment> dxkit-allow:<category>`
5
+ * comments and records each occurrence as a `(file, line, category)`
6
+ * tuple. The `stale-allow` producer uses this list together with the
7
+ * current scan's secret/code/config findings to detect orphaned
8
+ * annotations — annotations whose underlying finding is no longer
9
+ * present, which the developer should remove.
10
+ *
11
+ * Architectural posture:
12
+ *
13
+ * - File walk goes through the canonical `walkSourceFiles` helper
14
+ * so `.gitignore` + `.dxkit-ignore` + bundled defaults are
15
+ * honored uniformly. No custom recursion / exclusion logic
16
+ * (per CLAUDE.md G_v4_7).
17
+ * - Per-language comment marker comes from each pack's
18
+ * `LanguageSupport.commentSyntax` via the inline annotation
19
+ * parser. No hardcoded `'//'` / `'#'` literals in this module
20
+ * (arch-check rule 2 enforces).
21
+ * - Annotation parsing reuses `parseAnnotation` from `inline.ts`
22
+ * — single source of grammar truth.
23
+ *
24
+ * Test files ARE walked (intentional — annotations often live in
25
+ * test fixtures suppressing scanner findings against deliberate
26
+ * placeholder credentials). Auto-generated files are NOT walked
27
+ * (the developer doesn't author annotations in generated code, and
28
+ * `walkSourceFiles` already excludes them by default).
29
+ */
30
+ import { type AnnotationPosition } from './inline';
31
+ export interface InlineAllowlistOccurrence {
32
+ /** Project-relative POSIX path. */
33
+ readonly file: string;
34
+ /** 1-based line number where the annotation comment LIVES. For
35
+ * above-line annotations the value is the line just before the
36
+ * finding's target; for same-line it's the finding's own line. */
37
+ readonly line: number;
38
+ /** Category named in the annotation (`test-fixture` / `false-positive` /
39
+ * etc.). Free-form at gather level; the producer cross-checks
40
+ * against the canonical `AllowlistCategory` union. */
41
+ readonly category: string;
42
+ /** Where the annotation sits relative to the suppressed line. */
43
+ readonly position: AnnotationPosition;
44
+ }
45
+ export interface GatherInlineOpts {
46
+ /** Walk test files too. Default: true (test fixtures legitimately
47
+ * carry intentional placeholders + suppressions). */
48
+ readonly includeTests?: boolean;
49
+ }
50
+ /**
51
+ * Walk source files under `cwd` and collect every inline allowlist
52
+ * annotation occurrence. Cheap on small repos; on large ones the
53
+ * `walkSourceFiles` cache amortizes the cost across multiple gather
54
+ * passes within a single baseline-create run.
55
+ *
56
+ * Returns occurrences in stable order (file path lexicographic, then
57
+ * line ascending) so downstream deterministic-output requirements
58
+ * are satisfied automatically.
59
+ */
60
+ export declare function gatherInlineAllowlistAnnotations(cwd: string, opts?: GatherInlineOpts): InlineAllowlistOccurrence[];
61
+ //# sourceMappingURL=gather.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gather.d.ts","sourceRoot":"","sources":["../../src/allowlist/gather.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAOH,OAAO,EAA+C,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEhG,MAAM,WAAW,yBAAyB;IACxC,mCAAmC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;uEAEmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;2DAEuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B;0DACsD;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,gBAAqB,GAC1B,yBAAyB,EAAE,CA+C7B"}