@shomra/agent 0.3.13 → 0.3.14
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/package.json +1 -1
- package/shomra.mjs +59 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shomra/agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Shomra — adversarial assurance for AI agents, as a local-first CLI. Blocks dangerous tool-calls before they run, attacks your own guardrails to prove they hold, and gates AI artifacts in your editor and CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/shomra.mjs
CHANGED
|
@@ -219,7 +219,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
219
219
|
'apply', 'dry-run', 'global', 'local', 'trailer', 'evolve', 'report', 'init',
|
|
220
220
|
'no-suppress', 'no-baseline', 'no-policy', 'no-index', 'adaptive',
|
|
221
221
|
'fail-on-regression', 'fail-on-blocked', 'write', 'yes', 'stdin', 'quiet', 'help',
|
|
222
|
-
'check', 'checklist', 'pre-receive', 'uninstall',
|
|
222
|
+
'check', 'checklist', 'pre-receive', 'uninstall', 'save',
|
|
223
223
|
]);
|
|
224
224
|
// Flags that take a value (`--key value` or `--key=value`).
|
|
225
225
|
const VALUE_FLAGS = new Set([
|
|
@@ -230,6 +230,8 @@ const VALUE_FLAGS = new Set([
|
|
|
230
230
|
// `--fail-on <critical|high|medium>` lets CI gate below the default
|
|
231
231
|
// (blocked-only) exit code — e.g. fail the build on a HIGH finding.
|
|
232
232
|
'fail-on',
|
|
233
|
+
// `shomra design --save --subject KIND:id` — pin the analysis to a subject.
|
|
234
|
+
'subject', 'title', 'note', 'actor',
|
|
233
235
|
]);
|
|
234
236
|
const KNOWN_FLAGS = new Set([...BOOLEAN_FLAGS, ...VALUE_FLAGS]);
|
|
235
237
|
|
|
@@ -5067,7 +5069,7 @@ async function addPackage(flags, positional) {
|
|
|
5067
5069
|
|
|
5068
5070
|
// ── shomra design: threat-model a system before it exists ───────────────────
|
|
5069
5071
|
//
|
|
5070
|
-
// shomra design <file|dir|-> [--checklist] [--json] [--strict]
|
|
5072
|
+
// shomra design <file|dir|-> [--save --subject KIND:id] [--checklist] [--json] [--strict]
|
|
5071
5073
|
//
|
|
5072
5074
|
// The leftmost surface Shomra has. Everything else needs an artifact; this reads
|
|
5073
5075
|
// a DESCRIPTION — an RFC, a design doc, a Jira/Linear ticket, a PR body — and
|
|
@@ -5136,6 +5138,11 @@ async function cmdDesign(flags, positional) {
|
|
|
5136
5138
|
}
|
|
5137
5139
|
}
|
|
5138
5140
|
|
|
5141
|
+
// --save pins the analysis to the live capability manifest server-side, so the
|
|
5142
|
+
// dashboard and the CI gate can tell later that the system has moved past it.
|
|
5143
|
+
// Without it the analysis dies with the terminal it printed to.
|
|
5144
|
+
if (flags.save) await saveDesign(results, flags);
|
|
5145
|
+
|
|
5139
5146
|
// CRITICAL = untrusted input reaching execution or a destructive action. That
|
|
5140
5147
|
// is a hard fail even without --strict: it is the one shape where the attacker
|
|
5141
5148
|
// picks the action, and no amount of care in the implementation recovers it.
|
|
@@ -5143,6 +5150,55 @@ async function cmdDesign(flags, positional) {
|
|
|
5143
5150
|
else if (open.length && flags.strict) process.exitCode = 2;
|
|
5144
5151
|
}
|
|
5145
5152
|
|
|
5153
|
+
// ── shomra design --save --subject KIND:id ──────────────────────────────────
|
|
5154
|
+
//
|
|
5155
|
+
// ⚠ ONE SUBJECT, ONE DOCUMENT. A directory of RFCs produces one analysis each,
|
|
5156
|
+
// and pinning several of them to the same subject would leave the subject
|
|
5157
|
+
// described by whichever happened to be written last. The command refuses
|
|
5158
|
+
// rather than picking.
|
|
5159
|
+
async function saveDesign(results, flags) {
|
|
5160
|
+
const { apiKey, url } = resolveSettings(loadConfig());
|
|
5161
|
+
const subject = String(flags.subject ?? '').trim();
|
|
5162
|
+
const m = /^(AGENT|PROJECT|DESIGN):(.+)$/i.exec(subject);
|
|
5163
|
+
if (!m) {
|
|
5164
|
+
console.error(red('✗') + ' --save needs ' + bold('--subject KIND:id') + dim(' (AGENT | PROJECT | DESIGN)'));
|
|
5165
|
+
console.error(dim(' e.g. ') + 'shomra design docs/rfc.md --save --subject DESIGN:inbox-agent');
|
|
5166
|
+
process.exit(EXIT_USAGE);
|
|
5167
|
+
}
|
|
5168
|
+
if (results.length !== 1) {
|
|
5169
|
+
console.error(red('✗') + ` --save takes one document; ${results.length} were modelled.`);
|
|
5170
|
+
console.error(dim(' Save each against its own subject.'));
|
|
5171
|
+
process.exit(EXIT_USAGE);
|
|
5172
|
+
}
|
|
5173
|
+
if (!apiKey || !url) exitNotConfigured();
|
|
5174
|
+
|
|
5175
|
+
const [, kind, id] = m;
|
|
5176
|
+
const r = results[0];
|
|
5177
|
+
process.stdout.write(dim(' Saving to the platform… '));
|
|
5178
|
+
try {
|
|
5179
|
+
const res = await api(url, apiKey, '/threat-models/agent-author', {
|
|
5180
|
+
subjectKind: kind.toUpperCase(),
|
|
5181
|
+
subjectId: id,
|
|
5182
|
+
title: flags.title ? String(flags.title) : r.name,
|
|
5183
|
+
analysis: r,
|
|
5184
|
+
note: flags.note ? String(flags.note) : `Modelled from ${r.name}.`,
|
|
5185
|
+
actor: flags.actor ? String(flags.actor) : undefined,
|
|
5186
|
+
});
|
|
5187
|
+
const v = res?.version;
|
|
5188
|
+
const c = res?.coverage;
|
|
5189
|
+
console.log(green('saved') + dim(` — v${v?.seq ?? '?'}, ${v?.reviewState ?? 'IN_REVIEW'}`));
|
|
5190
|
+
// ⚠ Saving is not approving. A version nobody else has signed off does not
|
|
5191
|
+
// clear the gate, and saying so here is the difference between a stored
|
|
5192
|
+
// document and a control.
|
|
5193
|
+
console.log(dim(' It is not approved yet — a threat model must be reviewed by someone other than its author.'));
|
|
5194
|
+
if (c?.headline) console.log(dim(' ') + c.headline);
|
|
5195
|
+
} catch (e) {
|
|
5196
|
+
console.log(red('failed'));
|
|
5197
|
+
console.error(dim(' ') + (e?.message || String(e)));
|
|
5198
|
+
process.exitCode = 1;
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
|
|
5146
5202
|
const DESIGN_DOC_RE = /\.(md|markdown|txt|rst|adoc)$/i;
|
|
5147
5203
|
const DESIGN_MAX_DOCS = 50;
|
|
5148
5204
|
|
|
@@ -6606,7 +6662,7 @@ ${bold('COMMANDS')}
|
|
|
6606
6662
|
${cyan('doctor')} ${bold('Am I safe?')} Posture of this machine's AI setup ${dim('[--json]')}
|
|
6607
6663
|
|
|
6608
6664
|
${dim('Prevention — get in front of the model, not just behind it')}
|
|
6609
|
-
${cyan('design')} ${bold('Threat-model a system before it exists')} ${dim('<file|dir|-> [--checklist] [--strict] [--json]')}
|
|
6665
|
+
${cyan('design')} ${bold('Threat-model a system before it exists')} ${dim('<file|dir|-> [--save --subject KIND:id] [--checklist] [--strict] [--json]')}
|
|
6610
6666
|
${dim('Reads an RFC / design doc / ticket and says whether it closes a path from')}
|
|
6611
6667
|
${dim('untrusted input to a consequence, plus what must be true before it ships.')}
|
|
6612
6668
|
${dim('Pipe a ticket straight in: ')}${bold('gh issue view 42 --json body -q .body | shomra design -')}
|