@vyuhlabs/dxkit 2.9.3 → 2.10.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.
- package/CHANGELOG.md +170 -0
- package/README.md +9 -0
- package/dist/allowlist/annotate.d.ts +71 -0
- package/dist/allowlist/annotate.d.ts.map +1 -0
- package/dist/allowlist/annotate.js +105 -0
- package/dist/allowlist/annotate.js.map +1 -0
- package/dist/allowlist/cli.d.ts +6 -0
- package/dist/allowlist/cli.d.ts.map +1 -1
- package/dist/allowlist/cli.js +70 -37
- package/dist/allowlist/cli.js.map +1 -1
- package/dist/analyzers/dashboard/index.d.ts.map +1 -1
- package/dist/analyzers/dashboard/index.js +6 -1
- package/dist/analyzers/dashboard/index.js.map +1 -1
- package/dist/analyzers/developer/gather.d.ts +16 -0
- package/dist/analyzers/developer/gather.d.ts.map +1 -1
- package/dist/analyzers/developer/gather.js +2 -0
- package/dist/analyzers/developer/gather.js.map +1 -1
- package/dist/analyzers/developer/ownership.d.ts +86 -0
- package/dist/analyzers/developer/ownership.d.ts.map +1 -0
- package/dist/analyzers/developer/ownership.js +180 -0
- package/dist/analyzers/developer/ownership.js.map +1 -0
- package/dist/analyzers/health.d.ts.map +1 -1
- package/dist/analyzers/health.js +17 -2
- package/dist/analyzers/health.js.map +1 -1
- package/dist/analyzers/quality/detailed.d.ts +5 -1
- package/dist/analyzers/quality/detailed.d.ts.map +1 -1
- package/dist/analyzers/quality/detailed.js +30 -29
- package/dist/analyzers/quality/detailed.js.map +1 -1
- package/dist/analyzers/security/actions.d.ts.map +1 -1
- package/dist/analyzers/security/actions.js +13 -0
- package/dist/analyzers/security/actions.js.map +1 -1
- package/dist/analyzers/security/aggregator.d.ts +18 -0
- package/dist/analyzers/security/aggregator.d.ts.map +1 -1
- package/dist/analyzers/security/aggregator.js +28 -0
- package/dist/analyzers/security/aggregator.js.map +1 -1
- package/dist/analyzers/security/detailed.d.ts +7 -1
- package/dist/analyzers/security/detailed.d.ts.map +1 -1
- package/dist/analyzers/security/detailed.js +31 -15
- package/dist/analyzers/security/detailed.js.map +1 -1
- package/dist/analyzers/security/gather.d.ts.map +1 -1
- package/dist/analyzers/security/gather.js +6 -0
- package/dist/analyzers/security/gather.js.map +1 -1
- package/dist/analyzers/security/index.d.ts.map +1 -1
- package/dist/analyzers/security/index.js +81 -2
- package/dist/analyzers/security/index.js.map +1 -1
- package/dist/analyzers/security/scanner-drift.d.ts +21 -0
- package/dist/analyzers/security/scanner-drift.d.ts.map +1 -0
- package/dist/analyzers/security/scanner-drift.js +113 -0
- package/dist/analyzers/security/scanner-drift.js.map +1 -0
- package/dist/analyzers/security/shallow.d.ts.map +1 -1
- package/dist/analyzers/security/shallow.js +24 -2
- package/dist/analyzers/security/shallow.js.map +1 -1
- package/dist/analyzers/security/types.d.ts +38 -0
- package/dist/analyzers/security/types.d.ts.map +1 -1
- package/dist/analyzers/tests/detailed.d.ts +5 -1
- package/dist/analyzers/tests/detailed.d.ts.map +1 -1
- package/dist/analyzers/tests/detailed.js +27 -20
- package/dist/analyzers/tests/detailed.js.map +1 -1
- package/dist/analyzers/tools/graphify.d.ts +11 -0
- package/dist/analyzers/tools/graphify.d.ts.map +1 -1
- package/dist/analyzers/tools/graphify.js +429 -413
- package/dist/analyzers/tools/graphify.js.map +1 -1
- package/dist/analyzers/tools/grep-secrets.d.ts.map +1 -1
- package/dist/analyzers/tools/grep-secrets.js +9 -0
- package/dist/analyzers/tools/grep-secrets.js.map +1 -1
- package/dist/analyzers/tools/osv-scanner-fix.d.ts.map +1 -1
- package/dist/analyzers/tools/osv-scanner-fix.js +12 -1
- package/dist/analyzers/tools/osv-scanner-fix.js.map +1 -1
- package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
- package/dist/analyzers/tools/tool-registry.js +78 -43
- package/dist/analyzers/tools/tool-registry.js.map +1 -1
- package/dist/analyzers/tools/walk-source-files.d.ts +10 -0
- package/dist/analyzers/tools/walk-source-files.d.ts.map +1 -1
- package/dist/analyzers/tools/walk-source-files.js +14 -0
- package/dist/analyzers/tools/walk-source-files.js.map +1 -1
- package/dist/analyzers/types.d.ts +9 -0
- package/dist/analyzers/types.d.ts.map +1 -1
- package/dist/attribution/attribute.d.ts +57 -0
- package/dist/attribution/attribute.d.ts.map +1 -0
- package/dist/attribution/attribute.js +149 -0
- package/dist/attribution/attribute.js.map +1 -0
- package/dist/baseline/entry-to-located.d.ts +12 -5
- package/dist/baseline/entry-to-located.d.ts.map +1 -1
- package/dist/baseline/entry-to-located.js +21 -7
- package/dist/baseline/entry-to-located.js.map +1 -1
- package/dist/baseline/git-aware-match.d.ts +7 -5
- package/dist/baseline/git-aware-match.d.ts.map +1 -1
- package/dist/baseline/git-aware-match.js +78 -5
- package/dist/baseline/git-aware-match.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -5
- package/dist/cli.js.map +1 -1
- package/dist/explore/context-hook.d.ts +49 -29
- package/dist/explore/context-hook.d.ts.map +1 -1
- package/dist/explore/context-hook.js +304 -29
- package/dist/explore/context-hook.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +13 -7
- package/dist/generator.js.map +1 -1
- package/dist/ingest/snyk-policy.d.ts +22 -1
- package/dist/ingest/snyk-policy.d.ts.map +1 -1
- package/dist/ingest/snyk-policy.js +75 -18
- package/dist/ingest/snyk-policy.js.map +1 -1
- package/dist/languages/index.d.ts +28 -5
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +38 -7
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/typescript.d.ts.map +1 -1
- package/dist/languages/typescript.js +19 -0
- package/dist/languages/typescript.js.map +1 -1
- package/dist/reviewers-cli.d.ts +57 -0
- package/dist/reviewers-cli.d.ts.map +1 -0
- package/dist/reviewers-cli.js +263 -0
- package/dist/reviewers-cli.js.map +1 -0
- package/dist/scoring/dimensions/security.d.ts +17 -0
- package/dist/scoring/dimensions/security.d.ts.map +1 -1
- package/dist/scoring/dimensions/security.js +12 -0
- package/dist/scoring/dimensions/security.js.map +1 -1
- package/package.json +1 -1
- package/templates/.claude/skills/dxkit-action/SKILL.md +13 -2
- package/templates/.claude/skills/dxkit-allowlist/SKILL.md +9 -0
- package/templates/.claude/skills/dxkit-onboard/SKILL.md +2 -2
- package/templates/.claude/skills/dxkit-pr/SKILL.md +22 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseCodeowners = parseCodeowners;
|
|
37
|
+
exports.matchCodeowners = matchCodeowners;
|
|
38
|
+
exports.buildSuggestions = buildSuggestions;
|
|
39
|
+
exports.runReviewers = runReviewers;
|
|
40
|
+
/**
|
|
41
|
+
* `vyuh-dxkit reviewers` — suggest reviewers for a change, grounded on the
|
|
42
|
+
* active-owner model (dev-report git history) rather than naive last-touch
|
|
43
|
+
* blame, blended with CODEOWNERS.
|
|
44
|
+
*
|
|
45
|
+
* The differentiation over a platform's built-in suggested-reviewers: the
|
|
46
|
+
* candidates are activity-weighted (recent, sustained work ranks highest),
|
|
47
|
+
* scoped to who is still active (a departed owner is never silently
|
|
48
|
+
* suggested), bot-free, exclude the change's own author, and carry a
|
|
49
|
+
* bus-factor signal. CODEOWNERS, when present, is authoritative and merged
|
|
50
|
+
* in. Output renders names + GitHub @handles — never raw emails.
|
|
51
|
+
*
|
|
52
|
+
* Pure helpers (`parseCodeowners`, `matchCodeowners`, `buildSuggestions`)
|
|
53
|
+
* are unit-tested without git; `runReviewers` is the IO entry point.
|
|
54
|
+
*/
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
const child_process_1 = require("child_process");
|
|
58
|
+
const logger = __importStar(require("./logger"));
|
|
59
|
+
const ownership_1 = require("./analyzers/developer/ownership");
|
|
60
|
+
const gather_1 = require("./analyzers/developer/gather");
|
|
61
|
+
/**
|
|
62
|
+
* Parse a CODEOWNERS file. Each non-comment line is `<pattern> <owner>...`.
|
|
63
|
+
* Returns rules in file order; CODEOWNERS semantics are "last matching rule
|
|
64
|
+
* wins," so callers iterate in reverse.
|
|
65
|
+
*/
|
|
66
|
+
function parseCodeowners(content) {
|
|
67
|
+
const rules = [];
|
|
68
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
69
|
+
const line = raw.replace(/#.*$/, '').trim();
|
|
70
|
+
if (!line)
|
|
71
|
+
continue;
|
|
72
|
+
const parts = line.split(/\s+/);
|
|
73
|
+
const pattern = parts[0];
|
|
74
|
+
const owners = parts.slice(1).filter(Boolean);
|
|
75
|
+
if (pattern && owners.length > 0)
|
|
76
|
+
rules.push({ pattern, owners });
|
|
77
|
+
}
|
|
78
|
+
return rules;
|
|
79
|
+
}
|
|
80
|
+
/** Translate a CODEOWNERS pattern to a RegExp (subset: `*`, `**`, leading
|
|
81
|
+
* `/`, trailing `/`). Good enough for the common cases; unmatched exotic
|
|
82
|
+
* globs simply don't match (conservative — never over-claims ownership). */
|
|
83
|
+
function patternToRegExp(pattern) {
|
|
84
|
+
let p = pattern;
|
|
85
|
+
const anchored = p.startsWith('/');
|
|
86
|
+
if (anchored)
|
|
87
|
+
p = p.slice(1);
|
|
88
|
+
const dirOnly = p.endsWith('/');
|
|
89
|
+
if (dirOnly)
|
|
90
|
+
p = p.slice(0, -1);
|
|
91
|
+
// Tokenize so `**` -> `.*` and `*` -> `[^/]*` without a fragile placeholder;
|
|
92
|
+
// every other regex-special char is escaped.
|
|
93
|
+
let body = '';
|
|
94
|
+
for (let i = 0; i < p.length; i++) {
|
|
95
|
+
const c = p[i];
|
|
96
|
+
if (c === '*') {
|
|
97
|
+
if (p[i + 1] === '*') {
|
|
98
|
+
body += '.*';
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
body += '[^/]*';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (/[.+^${}()|[\]\\?]/.test(c)) {
|
|
106
|
+
body += '\\' + c;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
body += c;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const full = dirOnly ? `${body}/.*` : `${body}(?:/.*)?`;
|
|
113
|
+
return new RegExp(anchored ? `^${full}$` : `(?:^|/)${full}$`);
|
|
114
|
+
}
|
|
115
|
+
/** Owners for one file: the LAST matching CODEOWNERS rule wins. Empty when
|
|
116
|
+
* no rule matches. */
|
|
117
|
+
function matchCodeowners(rules, file) {
|
|
118
|
+
for (let i = rules.length - 1; i >= 0; i--) {
|
|
119
|
+
if (patternToRegExp(rules[i].pattern).test(file))
|
|
120
|
+
return [...rules[i].owners];
|
|
121
|
+
}
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Compose the active-owner ranking with CODEOWNERS into a ranked reviewer
|
|
126
|
+
* list. Pure. CODEOWNERS owners are authoritative (always included, flagged);
|
|
127
|
+
* active git owners follow, ranked by score; inactive owners are dropped from
|
|
128
|
+
* the suggestion list (they're surfaced only via the `note` fallback).
|
|
129
|
+
*/
|
|
130
|
+
function buildSuggestions(ownership, opts = {}) {
|
|
131
|
+
const limit = opts.limit ?? 3;
|
|
132
|
+
const codeowners = dedupe(opts.codeowners ?? []);
|
|
133
|
+
const out = [];
|
|
134
|
+
const seen = new Set();
|
|
135
|
+
// 1. CODEOWNERS first — authoritative.
|
|
136
|
+
for (const token of codeowners) {
|
|
137
|
+
const handle = token.startsWith('@') ? token.slice(1) : undefined;
|
|
138
|
+
const key = token.toLowerCase();
|
|
139
|
+
if (seen.has(key))
|
|
140
|
+
continue;
|
|
141
|
+
seen.add(key);
|
|
142
|
+
out.push({
|
|
143
|
+
name: token,
|
|
144
|
+
...(handle ? { handle } : {}),
|
|
145
|
+
active: true, // CODEOWNERS is a maintained, current declaration
|
|
146
|
+
isCodeowner: true,
|
|
147
|
+
reason: 'listed in CODEOWNERS for the touched paths',
|
|
148
|
+
score: Number.POSITIVE_INFINITY,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// 2. Active git owners, ranked.
|
|
152
|
+
const activeOwners = ownership.ranked.filter((o) => o.active);
|
|
153
|
+
for (const o of activeOwners) {
|
|
154
|
+
const handle = o.githubHandle;
|
|
155
|
+
const key = (handle ? `@${handle}` : o.name).toLowerCase();
|
|
156
|
+
if (seen.has(key))
|
|
157
|
+
continue;
|
|
158
|
+
seen.add(key);
|
|
159
|
+
out.push({
|
|
160
|
+
name: o.name,
|
|
161
|
+
...(handle ? { handle } : {}),
|
|
162
|
+
active: true,
|
|
163
|
+
isCodeowner: false,
|
|
164
|
+
reason: `${o.commits} recent commit${o.commits === 1 ? '' : 's'} to the touched files (last ${o.lastTouched})`,
|
|
165
|
+
score: o.score,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const reviewers = out.slice(0, limit);
|
|
169
|
+
let note;
|
|
170
|
+
if (activeOwners.length === 0 && codeowners.length === 0) {
|
|
171
|
+
note =
|
|
172
|
+
ownership.ranked.length > 0
|
|
173
|
+
? 'Every contributor who has touched these files is inactive — no current owner to suggest. Route by current team ownership.'
|
|
174
|
+
: 'No git history or CODEOWNERS for the touched files — no reviewer signal.';
|
|
175
|
+
}
|
|
176
|
+
else if (activeOwners.length === 0) {
|
|
177
|
+
note = 'Original authors are inactive — suggesting by CODEOWNERS only.';
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
touchedFiles: [],
|
|
181
|
+
reviewers,
|
|
182
|
+
busFactor: ownership.busFactor,
|
|
183
|
+
...(note ? { note } : {}),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function dedupe(xs) {
|
|
187
|
+
const seen = new Set();
|
|
188
|
+
const out = [];
|
|
189
|
+
for (const x of xs) {
|
|
190
|
+
const k = x.toLowerCase();
|
|
191
|
+
if (!seen.has(k)) {
|
|
192
|
+
seen.add(k);
|
|
193
|
+
out.push(x);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
function gitOut(cmd, cwd) {
|
|
199
|
+
try {
|
|
200
|
+
return (0, child_process_1.execSync)(cmd, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return '';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function touchedFiles(cwd, opts) {
|
|
207
|
+
if (opts.staged) {
|
|
208
|
+
return gitOut('git diff --cached --name-only', cwd).split('\n').filter(Boolean);
|
|
209
|
+
}
|
|
210
|
+
const base = opts.base || gitOut('git rev-parse --abbrev-ref origin/HEAD', cwd) || 'origin/main';
|
|
211
|
+
const out = gitOut(`git diff --name-only ${base}...HEAD`, cwd);
|
|
212
|
+
return out.split('\n').filter(Boolean);
|
|
213
|
+
}
|
|
214
|
+
function readCodeowners(cwd) {
|
|
215
|
+
for (const rel of ['.github/CODEOWNERS', 'CODEOWNERS', 'docs/CODEOWNERS']) {
|
|
216
|
+
const p = path.join(cwd, rel);
|
|
217
|
+
if (fs.existsSync(p))
|
|
218
|
+
return parseCodeowners(fs.readFileSync(p, 'utf8'));
|
|
219
|
+
}
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
function runReviewers(cwd, opts) {
|
|
223
|
+
const files = touchedFiles(cwd, opts);
|
|
224
|
+
if (files.length === 0) {
|
|
225
|
+
logger.info('No changed files detected (pass --base <ref> or --staged). Nothing to suggest.');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// Exclude the change author from suggestions (never review your own PR).
|
|
229
|
+
const authorEmail = gitOut('git config --get user.email', cwd);
|
|
230
|
+
const excludeEmails = authorEmail ? new Set([(0, gather_1.normalizeEmail)(authorEmail)]) : undefined;
|
|
231
|
+
const ownership = (0, ownership_1.ownersFor)(cwd, files, { ...(excludeEmails ? { excludeEmails } : {}) });
|
|
232
|
+
// CODEOWNERS owners for the touched files (deduped across files).
|
|
233
|
+
const rules = readCodeowners(cwd);
|
|
234
|
+
const coOwners = [];
|
|
235
|
+
for (const f of files)
|
|
236
|
+
coOwners.push(...matchCodeowners(rules, f));
|
|
237
|
+
const result = {
|
|
238
|
+
...buildSuggestions(ownership, {
|
|
239
|
+
...(opts.limit !== undefined ? { limit: opts.limit } : {}),
|
|
240
|
+
codeowners: coOwners,
|
|
241
|
+
}),
|
|
242
|
+
touchedFiles: files,
|
|
243
|
+
};
|
|
244
|
+
if (opts.json) {
|
|
245
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
logger.info(`Suggested reviewers for ${files.length} changed file${files.length === 1 ? '' : 's'}:`);
|
|
249
|
+
if (result.reviewers.length === 0) {
|
|
250
|
+
logger.info(' (none)');
|
|
251
|
+
}
|
|
252
|
+
for (const r of result.reviewers) {
|
|
253
|
+
const who = r.handle ? `@${r.handle}` : r.name;
|
|
254
|
+
const tag = r.isCodeowner ? ' [CODEOWNERS]' : '';
|
|
255
|
+
logger.info(` ${who}${tag} — ${r.reason}`);
|
|
256
|
+
}
|
|
257
|
+
if (result.busFactor === 1) {
|
|
258
|
+
logger.warn(' Bus factor 1: a single active owner covers these files — consider spreading knowledge.');
|
|
259
|
+
}
|
|
260
|
+
if (result.note)
|
|
261
|
+
logger.dim(` ${result.note}`);
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=reviewers-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewers-cli.js","sourceRoot":"","sources":["../src/reviewers-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -78,6 +78,23 @@ export interface SecurityScoreInput {
|
|
|
78
78
|
* Adapters MUST populate this from `DepVulnSummary.available`.
|
|
79
79
|
*/
|
|
80
80
|
depVulnsAvailable: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Pre-2.10 the unavailability cap was asymmetric — a missing
|
|
83
|
+
* dep-vuln scan capped at the uncertainty tier while missing
|
|
84
|
+
* secret/code scanners silently scored as "0 findings". An upgrade
|
|
85
|
+
* that merely turned the secret scanners ON then read as a score
|
|
86
|
+
* drop on an unchanged commit. These two flags give
|
|
87
|
+
* every measurement axis the same honest treatment: scanner didn't
|
|
88
|
+
* run → uncertainty cap, never a confident clean score.
|
|
89
|
+
*
|
|
90
|
+
* Same attempted-and-failed semantics as `depVulnsAvailable`:
|
|
91
|
+
* false ONLY when the gather was attempted and no provider
|
|
92
|
+
* succeeded. "No active provider" stays vacuously true.
|
|
93
|
+
*/
|
|
94
|
+
secretsAvailable: boolean;
|
|
95
|
+
/** See `secretsAvailable` — same contract for the semgrep /
|
|
96
|
+
* code-patterns axis. */
|
|
97
|
+
codePatternsAvailable: boolean;
|
|
81
98
|
}
|
|
82
99
|
export declare const SECURITY_SCORING_SPEC: DimensionScoringSpec<SecurityScoreInput>;
|
|
83
100
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../src/scoring/dimensions/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,oEAAoE;AACpE,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,eAAe,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,YAAY,EAAE,cAAc,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,iBAAiB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../src/scoring/dimensions/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,oEAAoE;AACpE,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,eAAe,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,YAAY,EAAE,cAAc,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,EAAE,cAAc,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,iBAAiB,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;;OAYG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAC1B;8BAC0B;IAC1B,qBAAqB,EAAE,OAAO,CAAC;CAChC;AAUD,eAAO,MAAM,qBAAqB,EAAE,oBAAoB,CAAC,kBAAkB,CAgG1E,CAAC"}
|
|
@@ -121,6 +121,18 @@ exports.SECURITY_SCORING_SPEC = {
|
|
|
121
121
|
describe: () => `dependency vulnerability scan did not run`,
|
|
122
122
|
applies: (i) => !i.depVulnsAvailable,
|
|
123
123
|
},
|
|
124
|
+
{
|
|
125
|
+
id: 'secrets-unavailable',
|
|
126
|
+
tier: 'uncertainty',
|
|
127
|
+
describe: () => `secret scan did not run`,
|
|
128
|
+
applies: (i) => !i.secretsAvailable,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'code-patterns-unavailable',
|
|
132
|
+
tier: 'uncertainty',
|
|
133
|
+
describe: () => `static-analysis (code-pattern) scan did not run`,
|
|
134
|
+
applies: (i) => !i.codePatternsAvailable,
|
|
135
|
+
},
|
|
124
136
|
{
|
|
125
137
|
id: 'high-plus-code-open',
|
|
126
138
|
tier: 'fixable-finding',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/scoring/dimensions/security.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;;;
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/scoring/dimensions/security.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -28,6 +28,8 @@ npx vyuh-dxkit vulnerabilities --detailed --graph-context # or test-gaps / qua
|
|
|
28
28
|
|
|
29
29
|
`--graph-context` adds a "Graph context" column (the module a finding lives in + its blast radius — how many files call into it) so you can plan the fix without separately discovering structure. It's a structural HINT, not ground truth — read "Graph context" below for how to use it safely.
|
|
30
30
|
|
|
31
|
+
Add **`--attribute`** when you need to know *who to ask* about a pre-existing finding — it adds a "Who to ask" column from `git blame` routed through the active-owner model, so an inactive author is forwarded to the file's current owner (names + @handles, never emails). It's opt-in and **historical only**: a net-new finding the guardrail just blocked was introduced by your own change, so "who to ask" there is simply the PR author — no blame needed. Honor the honesty caveat (blame is last-touch, not necessarily who introduced it).
|
|
32
|
+
|
|
31
33
|
## Scoped fix — fix one category at a time
|
|
32
34
|
|
|
33
35
|
Often the user doesn't want "fix everything" — they want to burn down **one
|
|
@@ -99,7 +101,16 @@ npx vyuh-dxkit vulnerabilities --json | jq '.summary.findings'
|
|
|
99
101
|
|
|
100
102
|
Don't try to redact the secret in place — the git history still has it. Rotation is the only true fix.
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
#### Secrets in test files — TRIAGE, don't assume
|
|
105
|
+
|
|
106
|
+
dxkit deliberately does **not** lower a secret's severity just because it sits in a test file: the scanner can't tell a throwaway fixture from a real credential someone pasted into an integration test, and a real leak in a test is still a leak. So test-file secrets reach you at full severity, and the report flags how many are test-located — that's your cue to triage each one, not to dismiss them:
|
|
107
|
+
|
|
108
|
+
1. **Open the line and judge the value.** Obvious throwaway fixture (`'password1'`, `'changeme'`, `'sk_test_xxxx'`, low-entropy / dictionary)? Or does it look like a real credential (high-entropy token, a real-looking host/user/password, something that would actually authenticate)?
|
|
109
|
+
2. **Fixture → allowlist it.** `vyuh-dxkit allowlist add <file>:<line> --category=test-fixture --reason="..."`. This is the right tool: it records *why*, and `test-fixture` (like `false-positive`) **lifts the finding from the Security score**, not just the guardrail — so a properly-triaged test file stops dragging the score down. Don't waste effort "rotating" a fake value.
|
|
110
|
+
3. **Real credential → treat it as a live leak.** Rotate in the provider, remove from the file, scrub git history (`git filter-repo` / BFG). Being in a test does not make it safe.
|
|
111
|
+
4. **Unsure? Ask the developer** before allowlisting — never allowlist a value you haven't confirmed is fake.
|
|
112
|
+
|
|
113
|
+
Allowlisting under `test-fixture` is a deliberate per-finding judgment, not a blanket "ignore tests" — don't add a `.dxkit-ignore` for the test directory to make the noise go away (that also drops the files from coverage + test-gap analysis, and would hide a real leak the same way). See "Allowlisting (when fix is not viable)" below.
|
|
103
114
|
|
|
104
115
|
### SAST finding (semgrep)
|
|
105
116
|
|
|
@@ -164,7 +175,7 @@ If the finding is a false positive, add `// slop-ok: <reason>` on the offending
|
|
|
164
175
|
|
|
165
176
|
## Allowlisting (when fix is not viable)
|
|
166
177
|
|
|
167
|
-
**Fix first.** The allowlist is the SECOND option, not the first. When you reach for it, choose deliberately — every allowlist entry is a future maintenance burden the
|
|
178
|
+
**Fix first.** The allowlist is the SECOND option, not the first. When you reach for it, choose deliberately — every allowlist entry is a future maintenance burden the team will revisit.
|
|
168
179
|
|
|
169
180
|
Five typed categories signal WHY the suppression is in place:
|
|
170
181
|
|
|
@@ -7,6 +7,15 @@ description: Manage the dxkit allowlist over its whole lifecycle — list, inspe
|
|
|
7
7
|
|
|
8
8
|
The allowlist is dxkit's per-finding suppression surface: a reviewed finding that the team has categorized (`false-positive`, `test-fixture`, `mitigated-externally`, `accepted-risk`, `deferred`) with a reason, so the guardrail lets it pass on future runs. It's the single source of truth across every scanner — native semgrep/gitleaks and ingested Snyk Code / CodeQL findings alike, all keyed on one fingerprint.
|
|
9
9
|
|
|
10
|
+
### Categories affect the score, not just the guardrail
|
|
11
|
+
|
|
12
|
+
The category isn't just a label — it decides whether the finding still counts toward the **Security score**:
|
|
13
|
+
|
|
14
|
+
- **`false-positive` / `test-fixture`** declare the finding is *not a real finding* (a scanner misfire, or throwaway test data). These are **lifted from the Security penalties and caps**, so a repo that has genuinely triaged its noise scores honestly instead of staying capped on findings it has already reviewed and accepted. This is also why test-file secrets (which dxkit never auto-downgrades by path) should be allowlisted as `test-fixture` once confirmed fake — that's what removes them from the score.
|
|
15
|
+
- **`accepted-risk` / `deferred` / `mitigated-externally`** accept a *real* exposure. The guardrail stops blocking, but the score keeps counting them — accepting a real risk can't earn an A. (`accepted-risk` / `deferred` also require an expiry so the acceptance ages out.)
|
|
16
|
+
|
|
17
|
+
So `false-positive`/`test-fixture` are the only categories that recover score. Reserve them for findings that genuinely aren't real — miscategorizing a real risk as `false-positive` to lift the score is exactly the self-deception the typed categories exist to prevent.
|
|
18
|
+
|
|
10
19
|
This skill manages the allowlist's **lifecycle**: reviewing what's there, keeping it honest, and propagating decisions outward. For the upstream question — *should this be fixed instead of suppressed, and how do I add an entry* — that decision and the `add` path live in **dxkit-action**. Fix first; suppress second.
|
|
11
20
|
|
|
12
21
|
## The lifecycle at a glance
|
|
@@ -104,7 +104,7 @@ For each fixable signal in doctor's output, dispatch through `dxkit-fix`'s recov
|
|
|
104
104
|
|
|
105
105
|
- `git hooks active` not active → `npx vyuh-dxkit hooks activate`
|
|
106
106
|
- `baseline captured` missing → defer to step 5 (we'll handle that explicitly with the secrets warning)
|
|
107
|
-
- `vyuh-dxkit on PATH` missing → `npm install -g @vyuhlabs/dxkit`
|
|
107
|
+
- `vyuh-dxkit on PATH` missing → `npm install -g @vyuhlabs/dxkit` (a global install — it affects every Node project on the machine and may need elevated permissions; a project-local install or a Node version manager works too)
|
|
108
108
|
- `scanner toolchain` incomplete → `npx vyuh-dxkit tools install --yes`
|
|
109
109
|
|
|
110
110
|
Don't auto-execute baseline capture here — step 5 has a values-laden warning that needs explicit customer confirmation.
|
|
@@ -195,7 +195,7 @@ Local hooks are fast feedback; CI is the unbypassable enforcement. Branch protec
|
|
|
195
195
|
|
|
196
196
|
ASK:
|
|
197
197
|
|
|
198
|
-
> **Configure branch protection now?** Default yes.
|
|
198
|
+
> **Configure branch protection now?** Default yes. This **modifies your GitHub repository settings** — it adds `dxkit-guardrails` as a required status check on `<default branch>`, and so needs admin permission on the repo. Without it, the CI workflow is informational — PRs can merge even on guardrail failures.
|
|
199
199
|
|
|
200
200
|
If yes:
|
|
201
201
|
|
|
@@ -78,6 +78,22 @@ Put in the body:
|
|
|
78
78
|
- **Score deltas** — only when the change targeted a dimension (e.g. "Tests
|
|
79
79
|
62 → 71 after closing the auth gaps"). Don't pad with unchanged scores.
|
|
80
80
|
|
|
81
|
+
### Suggested reviewers
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx vyuh-dxkit reviewers --base <base-branch> --json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This ranks reviewers by the **active-owner model** — recency-weighted git
|
|
88
|
+
history on the touched files, with bots and departed contributors filtered, the
|
|
89
|
+
PR author excluded, blended with `CODEOWNERS`. Better signal than the platform's
|
|
90
|
+
naive last-touch suggestion. Surface the top few in the body with the *why*
|
|
91
|
+
("@alice — owns 3/4 touched files, active"), and you can pass them to
|
|
92
|
+
`gh pr create --reviewer`. Honor the output's caveats: if it returns a
|
|
93
|
+
`busFactor: 1`, note the single-point-of-failure; if it returns a `note`
|
|
94
|
+
(original authors inactive / no signal), say so rather than inventing a
|
|
95
|
+
reviewer. Renders `@handle`s, never emails.
|
|
96
|
+
|
|
81
97
|
## [4] Draft — title, body, reviewer checklist
|
|
82
98
|
|
|
83
99
|
**Title** — imperative, scoped, specific. `feat(auth): add refresh-token
|
|
@@ -100,6 +116,10 @@ rotation`, not `Updates`. Match the repo's existing PR/commit convention
|
|
|
100
116
|
- Allowlist: <new suppressions + reason + expiry, or "no changes">
|
|
101
117
|
- Scores: <dimension deltas, if the change targeted one>
|
|
102
118
|
|
|
119
|
+
## Suggested reviewers
|
|
120
|
+
- @alice — owns 3/4 touched files, active · @bob — CODEOWNERS
|
|
121
|
+
(or the `note` when there's no active-owner signal)
|
|
122
|
+
|
|
103
123
|
## Reviewer checklist
|
|
104
124
|
- [ ] Change matches the description; scope isn't broader than stated
|
|
105
125
|
- [ ] <feature>: behavior verified (how to exercise it)
|
|
@@ -120,7 +140,8 @@ Show the user the full draft (title + body) and confirm. On yes:
|
|
|
120
140
|
|
|
121
141
|
```bash
|
|
122
142
|
git push -u origin HEAD # if not already pushed
|
|
123
|
-
gh pr create --base main --title "<title>" --body "<body>"
|
|
143
|
+
gh pr create --base main --title "<title>" --body "<body>" \
|
|
144
|
+
--reviewer <handle1>,<handle2> # the active owners from `reviewers`, if any
|
|
124
145
|
```
|
|
125
146
|
|
|
126
147
|
If `gh` isn't authenticated, print the title + body for the user to paste, and
|