atris 3.29.0 → 3.30.1
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/commands/security-review.js +64 -0
- package/commands/task.js +730 -23
- package/commands/workflow.js +6 -6
- package/commands/xp.js +10 -1
- package/lib/file-ops.js +1 -1
- package/lib/journal.js +7 -73
- package/lib/security-scan.js +73 -1
- package/lib/task-db.js +20 -10
- package/package.json +1 -1
|
@@ -31,6 +31,8 @@ const {
|
|
|
31
31
|
applyBaseline,
|
|
32
32
|
shouldFail,
|
|
33
33
|
scoreFindings,
|
|
34
|
+
recordRun,
|
|
35
|
+
buildLanding,
|
|
34
36
|
} = require('../lib/security-scan');
|
|
35
37
|
|
|
36
38
|
const ICON = { critical: '✗', high: '✗', medium: '!', low: '·', privacy: '✗', secret: '✗', pii: '!' };
|
|
@@ -46,6 +48,8 @@ function parseArgs(argv) {
|
|
|
46
48
|
noBaseline: false,
|
|
47
49
|
deep: false,
|
|
48
50
|
md: false,
|
|
51
|
+
land: false,
|
|
52
|
+
noRecord: false,
|
|
49
53
|
baseline: DEFAULT_BASELINE,
|
|
50
54
|
paths: [],
|
|
51
55
|
};
|
|
@@ -60,6 +64,8 @@ function parseArgs(argv) {
|
|
|
60
64
|
else if (arg === '--no-baseline') opts.noBaseline = true;
|
|
61
65
|
else if (arg === '--deep') opts.deep = true;
|
|
62
66
|
else if (arg === '--md' || arg === '--markdown') opts.md = true;
|
|
67
|
+
else if (arg === '--land' || arg === '--landing') opts.land = true;
|
|
68
|
+
else if (arg === '--no-record') opts.noRecord = true;
|
|
63
69
|
else if (arg === '--baseline') {
|
|
64
70
|
if (!argv[i + 1] || argv[i + 1].startsWith('-')) throw new Error('--baseline requires a path');
|
|
65
71
|
opts.baseline = argv[++i];
|
|
@@ -144,6 +150,13 @@ function securityReviewCommand(argv = []) {
|
|
|
144
150
|
const failing = failingCount(findings, threshold);
|
|
145
151
|
const ok = !shouldFail(findings, threshold);
|
|
146
152
|
|
|
153
|
+
// Flight recorder + landing: the loop appends one row per run; the landing
|
|
154
|
+
// compares this run to the last one. buildLanding must read the ledger BEFORE
|
|
155
|
+
// recordRun appends this run.
|
|
156
|
+
const scanResult = { findings, counts, scanned: raw.scanned, suppressed: result.suppressed };
|
|
157
|
+
const landing = buildLanding(root, scanResult, { failOn: threshold });
|
|
158
|
+
if (!opts.noRecord) recordRun(root, scanResult, { failOn: threshold });
|
|
159
|
+
|
|
147
160
|
if (opts.json) {
|
|
148
161
|
console.log(JSON.stringify({
|
|
149
162
|
ok,
|
|
@@ -155,6 +168,7 @@ function securityReviewCommand(argv = []) {
|
|
|
155
168
|
score: scoreFindings(findings),
|
|
156
169
|
score_all: scoreFindings(allFindings),
|
|
157
170
|
fail_threshold: threshold,
|
|
171
|
+
landing,
|
|
158
172
|
findings,
|
|
159
173
|
deep_review: opts.deep ? deepReviewPayload({ findings, counts, scanned: raw.scanned, suppressed: result.suppressed }) : undefined,
|
|
160
174
|
generated_for: 'soc2-evidence',
|
|
@@ -178,6 +192,11 @@ function securityReviewCommand(argv = []) {
|
|
|
178
192
|
return ok ? 0 : 1;
|
|
179
193
|
}
|
|
180
194
|
|
|
195
|
+
if (opts.land) {
|
|
196
|
+
console.log(renderLanding(landing, threshold));
|
|
197
|
+
return ok ? 0 : 1;
|
|
198
|
+
}
|
|
199
|
+
|
|
181
200
|
if (!opts.quiet) {
|
|
182
201
|
console.log('\n ◉ atris security review');
|
|
183
202
|
if (!findings.length) {
|
|
@@ -205,6 +224,49 @@ function securityReviewCommand(argv = []) {
|
|
|
205
224
|
return ok ? 0 : 1;
|
|
206
225
|
}
|
|
207
226
|
|
|
227
|
+
// The landing: short, true, decision-ready. What you read after the overnight
|
|
228
|
+
// loop — the opposite of a finding dump.
|
|
229
|
+
function renderLanding(landing, threshold) {
|
|
230
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
231
|
+
const L = ['', ` ✈ security landing — ${date}`, ''];
|
|
232
|
+
if (landing.cleared) {
|
|
233
|
+
L.push(` CLEARED TO SHIP no unresolved findings at the ${threshold.toUpperCase()} line`);
|
|
234
|
+
} else {
|
|
235
|
+
const n = landing.open.length;
|
|
236
|
+
L.push(` HOLD ${n} finding${n === 1 ? '' : 's'} need a decision before ship`);
|
|
237
|
+
}
|
|
238
|
+
L.push('');
|
|
239
|
+
if (landing.hadPrevRun) {
|
|
240
|
+
L.push(' since last run:');
|
|
241
|
+
L.push(` fixed ${landing.fixed}`);
|
|
242
|
+
L.push(` new ${landing.appeared}`);
|
|
243
|
+
L.push(` accepted ${landing.accepted} (known-safe, in baseline)`);
|
|
244
|
+
} else {
|
|
245
|
+
L.push(` first run: ${landing.open.length} open · ${landing.accepted} accepted (baseline)`);
|
|
246
|
+
}
|
|
247
|
+
L.push('');
|
|
248
|
+
if (landing.open.length) {
|
|
249
|
+
L.push(' needs you:');
|
|
250
|
+
for (const f of landing.open.slice(0, 10)) {
|
|
251
|
+
L.push(` ${f.sev.toUpperCase().padEnd(8)} ${f.file}${f.line ? ':' + f.line : ''} — ${f.why}`);
|
|
252
|
+
}
|
|
253
|
+
if (landing.open.length > 10) L.push(` … and ${landing.open.length - 10} more`);
|
|
254
|
+
} else {
|
|
255
|
+
L.push(' needs you: nothing');
|
|
256
|
+
}
|
|
257
|
+
L.push('');
|
|
258
|
+
if (landing.trend.length > 1) {
|
|
259
|
+
const a = landing.trend[0], b = landing.trend[landing.trend.length - 1];
|
|
260
|
+
const da = a.critical + a.high, db = b.critical + b.high;
|
|
261
|
+
const dir = db < da ? 'improving' : db > da ? 'worsening' : 'steady';
|
|
262
|
+
L.push(` posture: critical ${a.critical}→${b.critical}, high ${a.high}→${b.high} over ${landing.trend.length} runs (${dir})`);
|
|
263
|
+
} else {
|
|
264
|
+
L.push(` posture: ${landing.runs} run${landing.runs === 1 ? '' : 's'} recorded · scanned ${landing.scanned} files`);
|
|
265
|
+
}
|
|
266
|
+
L.push('');
|
|
267
|
+
return L.join('\n');
|
|
268
|
+
}
|
|
269
|
+
|
|
208
270
|
function printRules() {
|
|
209
271
|
console.log('\n atris security-review — deterministic rules:\n');
|
|
210
272
|
for (const sev of SEVERITIES) {
|
|
@@ -343,6 +405,8 @@ function printHelp() {
|
|
|
343
405
|
atris security-review --json machine output / SOC 2 evidence artifact
|
|
344
406
|
atris security-review --md markdown evidence report
|
|
345
407
|
atris security-review --deep prompt a stronger model with framework + evidence
|
|
408
|
+
atris security-review --land the landing: cleared-to-ship or hold, what changed,
|
|
409
|
+
what needs you, the trend (read this after the loop)
|
|
346
410
|
atris security-review --update-baseline
|
|
347
411
|
accept current findings in .security-review.baseline.json
|
|
348
412
|
atris security-review --no-baseline
|