@zivis/cli 0.1.0-alpha.33 → 0.1.0-alpha.35
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/LICENSE +201 -0
- package/README.md +31 -0
- package/dist/commands/app/index.d.ts +0 -8
- package/dist/commands/app/index.js +0 -12
- package/dist/commands/auth/bind.d.ts +0 -10
- package/dist/commands/auth/bind.js +0 -11
- package/dist/commands/auth/index.js +0 -1
- package/dist/commands/auth/init.js +12 -13
- package/dist/commands/auth/login.d.ts +0 -1
- package/dist/commands/auth/login.js +8 -3
- package/dist/commands/auth/logout.js +0 -3
- package/dist/commands/auth/sessions.js +0 -1
- package/dist/commands/auth/status.js +0 -2
- package/dist/commands/auth/token.d.ts +0 -13
- package/dist/commands/auth/token.js +0 -18
- package/dist/commands/check/index.d.ts +2 -0
- package/dist/commands/check/index.js +463 -0
- package/dist/commands/credits/index.js +0 -1
- package/dist/commands/inspect/index.d.ts +0 -15
- package/dist/commands/inspect/index.js +0 -23
- package/dist/commands/mcp/index.js +0 -9
- package/dist/commands/open/index.d.ts +0 -8
- package/dist/commands/open/index.js +0 -11
- package/dist/commands/scan/index.js +0 -6
- package/dist/commands/target/index.js +0 -3
- package/dist/commands/tm/index.js +0 -7
- package/dist/index.js +11 -1
- package/dist/internal/binding-token-mint.d.ts +0 -29
- package/dist/internal/binding-token-mint.js +0 -28
- package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
- package/dist/internal/extractor/adapters/go/index.js +0 -6
- package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
- package/dist/internal/extractor/adapters/go/manifest.js +1 -25
- package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
- package/dist/internal/extractor/adapters/go/parser.js +0 -5
- package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
- package/dist/internal/extractor/adapters/go/scanner.js +0 -43
- package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
- package/dist/internal/extractor/adapters/python/index.js +0 -4
- package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
- package/dist/internal/extractor/adapters/python/manifest.js +3 -18
- package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
- package/dist/internal/extractor/adapters/python/parser.js +0 -4
- package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
- package/dist/internal/extractor/adapters/python/scanner.js +1 -54
- package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
- package/dist/internal/extractor/adapters/typescript/index.js +1 -21
- package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
- package/dist/internal/extractor/adapters/typescript/manifest.js +1 -20
- package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
- package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
- package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
- package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
- package/dist/internal/extractor/graph-builder.d.ts +0 -25
- package/dist/internal/extractor/graph-builder.js +1 -39
- package/dist/internal/extractor/graph-types.d.ts +0 -10
- package/dist/internal/extractor/graph-types.js +0 -11
- package/dist/internal/extractor/index.d.ts +0 -14
- package/dist/internal/extractor/index.js +0 -13
- package/dist/internal/extractor/types.d.ts +0 -45
- package/dist/internal/extractor/types.js +0 -27
- package/dist/internal/extractor/walker.d.ts +0 -18
- package/dist/internal/extractor/walker.js +0 -18
- package/dist/internal/github-linkage-prompt.d.ts +7 -0
- package/dist/internal/github-linkage-prompt.js +95 -0
- package/dist/internal/ide-setup.d.ts +0 -23
- package/dist/internal/ide-setup.js +0 -53
- package/dist/internal/orgs.js +0 -1
- package/dist/internal/project-binding.d.ts +0 -1
- package/dist/internal/project-binding.js +0 -5
- package/dist/internal/target-auth/api-config.d.ts +0 -17
- package/dist/internal/target-auth/api-config.js +0 -18
- package/dist/internal/target-auth/config.d.ts +0 -12
- package/dist/internal/target-auth/config.js +0 -13
- package/dist/internal/target-auth/index.d.ts +0 -18
- package/dist/internal/target-auth/index.js +0 -19
- package/dist/internal/target-auth/keychain.d.ts +0 -18
- package/dist/internal/target-auth/keychain.js +3 -23
- package/dist/internal/target-auth/types.d.ts +0 -26
- package/dist/internal/target-auth/types.js +0 -10
- package/dist/internal/target-auth/workos.d.ts +0 -15
- package/dist/internal/target-auth/workos.js +0 -17
- package/package.json +13 -4
- package/dist/commands/app/index.js.map +0 -1
- package/dist/commands/auth/bind.js.map +0 -1
- package/dist/commands/auth/index.js.map +0 -1
- package/dist/commands/auth/init.js.map +0 -1
- package/dist/commands/auth/login.js.map +0 -1
- package/dist/commands/auth/logout.js.map +0 -1
- package/dist/commands/auth/sessions.js.map +0 -1
- package/dist/commands/auth/status.js.map +0 -1
- package/dist/commands/auth/token.js.map +0 -1
- package/dist/commands/credits/index.js.map +0 -1
- package/dist/commands/inspect/index.js.map +0 -1
- package/dist/commands/mcp/index.js.map +0 -1
- package/dist/commands/open/index.js.map +0 -1
- package/dist/commands/scan/index.js.map +0 -1
- package/dist/commands/target/index.js.map +0 -1
- package/dist/commands/tm/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/binding-token-mint.js.map +0 -1
- package/dist/internal/extractor/adapters/go/index.js.map +0 -1
- package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
- package/dist/internal/extractor/adapters/python/index.js.map +0 -1
- package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
- package/dist/internal/extractor/graph-builder.js.map +0 -1
- package/dist/internal/extractor/graph-types.js.map +0 -1
- package/dist/internal/extractor/index.js.map +0 -1
- package/dist/internal/extractor/types.js.map +0 -1
- package/dist/internal/extractor/walker.js.map +0 -1
- package/dist/internal/ide-setup.js.map +0 -1
- package/dist/internal/orgs.js.map +0 -1
- package/dist/internal/pattern-pack/index.d.ts +0 -123
- package/dist/internal/pattern-pack/index.js +0 -230
- package/dist/internal/pattern-pack/index.js.map +0 -1
- package/dist/internal/project-binding.js.map +0 -1
- package/dist/internal/target-auth/api-config.js.map +0 -1
- package/dist/internal/target-auth/config.js.map +0 -1
- package/dist/internal/target-auth/index.js.map +0 -1
- package/dist/internal/target-auth/keychain.js.map +0 -1
- package/dist/internal/target-auth/types.js.map +0 -1
- package/dist/internal/target-auth/workos.js.map +0 -1
- package/dist/pattern-packs/zivis-public-0.1.0/capsules/agents/privilege-separation.yaml +0 -228
- package/dist/pattern-packs/zivis-public-0.1.0/manifest.json +0 -31
- package/dist/pattern-packs/zivis-public-0.1.0/prompts/agents/privilege-separation/identify.md +0 -54
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as readline from "node:readline";
|
|
3
|
+
import * as fs from "node:fs/promises";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import * as os from "node:os";
|
|
6
|
+
import { exec } from "node:child_process";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
import { resolveAuth } from "@zivis/mcp/auth";
|
|
9
|
+
import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
|
|
10
|
+
import { DEFAULT_CONFIG } from "@zivis/mcp/types";
|
|
11
|
+
import { runMatcher } from "@zivis/mcp/matcher";
|
|
12
|
+
import { runExtractor } from "../../internal/extractor/index.js";
|
|
13
|
+
import { buildRepoGraphArtifact } from "../../internal/extractor/graph-builder.js";
|
|
14
|
+
import { loginCommand } from "../auth/login.js";
|
|
15
|
+
import { buildLinkagePrompt } from "../../internal/github-linkage-prompt.js";
|
|
16
|
+
const execAsync = promisify(exec);
|
|
17
|
+
const LLM_PACKAGE_NAMES = new Set([
|
|
18
|
+
"openai",
|
|
19
|
+
"@anthropic-ai/sdk",
|
|
20
|
+
"anthropic",
|
|
21
|
+
"@anthropic-ai/bedrock-sdk",
|
|
22
|
+
"@anthropic-ai/vertex-sdk",
|
|
23
|
+
"@google/generative-ai",
|
|
24
|
+
"@google-cloud/aiplatform",
|
|
25
|
+
"@google-cloud/vertexai",
|
|
26
|
+
"@aws-sdk/client-bedrock-runtime",
|
|
27
|
+
"@azure/openai",
|
|
28
|
+
"langchain",
|
|
29
|
+
"@langchain/core",
|
|
30
|
+
"@langchain/openai",
|
|
31
|
+
"@langchain/anthropic",
|
|
32
|
+
"llamaindex",
|
|
33
|
+
"ai",
|
|
34
|
+
"ollama",
|
|
35
|
+
"cohere-ai",
|
|
36
|
+
"@cohere-ai/sdk",
|
|
37
|
+
"replicate",
|
|
38
|
+
"mistralai",
|
|
39
|
+
"@mistralai/mistralai",
|
|
40
|
+
"groq-sdk",
|
|
41
|
+
"@groq/groq-sdk",
|
|
42
|
+
]);
|
|
43
|
+
export function registerCheckCommands(program) {
|
|
44
|
+
program
|
|
45
|
+
.command("check")
|
|
46
|
+
.description("inspect this repo's architecture and post findings to your ZIVIS threat model")
|
|
47
|
+
.option("--cwd <path>", "repo root to inspect (default: cwd)")
|
|
48
|
+
.option("--format <fmt>", "output format: human or json", "human")
|
|
49
|
+
.option("--no-network", "skip server-side promotion; build the graph + run matcher locally only")
|
|
50
|
+
.action(async (opts) => {
|
|
51
|
+
const result = await runCheck(opts);
|
|
52
|
+
process.exit(result.exitCode);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function runCheck(opts) {
|
|
56
|
+
const rootDir = opts.cwd ?? process.cwd();
|
|
57
|
+
const format = opts.format === "json" ? "json" : "human";
|
|
58
|
+
const network = opts.network !== false;
|
|
59
|
+
const config = { ...DEFAULT_CONFIG, ...resolveConfigFromBinding(rootDir) };
|
|
60
|
+
const authStatus = await resolveAuth(config);
|
|
61
|
+
if (authStatus.method === "none") {
|
|
62
|
+
const handled = await handleUnauthenticated({ format, config });
|
|
63
|
+
if (handled !== "continue") {
|
|
64
|
+
return { exitCode: handled };
|
|
65
|
+
}
|
|
66
|
+
const recheck = await resolveAuth(config);
|
|
67
|
+
if (recheck.method === "none") {
|
|
68
|
+
printIfHuman(format, "\nAuth did not complete. Run `zivis auth login` and try again.");
|
|
69
|
+
return { exitCode: 1 };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const creds = await resolveAuth(config);
|
|
73
|
+
const startedAt = Date.now();
|
|
74
|
+
const extractorOutput = await runExtractor({
|
|
75
|
+
rootDir,
|
|
76
|
+
extractorVersion: "check",
|
|
77
|
+
});
|
|
78
|
+
const gitMeta = await readGitMetadata(rootDir);
|
|
79
|
+
const binding = detectProjectBinding(rootDir);
|
|
80
|
+
const applicationId = binding?.binding.applicationId;
|
|
81
|
+
const repoId = deriveRepoId(gitMeta.remoteUrl, rootDir);
|
|
82
|
+
const artifact = buildRepoGraphArtifact(extractorOutput, {
|
|
83
|
+
applicationId,
|
|
84
|
+
repoId,
|
|
85
|
+
commitSha: gitMeta.commitSha,
|
|
86
|
+
branch: gitMeta.branch,
|
|
87
|
+
analysisMode: "local",
|
|
88
|
+
});
|
|
89
|
+
const artifactPath = await writeArtifactToCache(artifact);
|
|
90
|
+
let matcherFindings = [];
|
|
91
|
+
try {
|
|
92
|
+
const matcherResult = await runMatcher({ artifactPath, cwd: rootDir });
|
|
93
|
+
matcherFindings = matcherResult.findings;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
97
|
+
process.stderr.write(`\n[zivis check] matcher failed (continuing without pattern matches): ${msg}\n`);
|
|
98
|
+
}
|
|
99
|
+
const durationMs = Date.now() - startedAt;
|
|
100
|
+
const summary = summarizeArtifact(artifact, matcherFindings);
|
|
101
|
+
let promotion;
|
|
102
|
+
let promotionError;
|
|
103
|
+
if (network) {
|
|
104
|
+
try {
|
|
105
|
+
promotion = await postCheckArtifact({
|
|
106
|
+
config,
|
|
107
|
+
creds,
|
|
108
|
+
artifact,
|
|
109
|
+
findings: matcherFindings,
|
|
110
|
+
repoName: deriveRepoNameFromGit(gitMeta.remoteUrl, rootDir),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
promotionError = err instanceof Error ? err.message : String(err);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
let linkagePrompt = null;
|
|
118
|
+
if (network) {
|
|
119
|
+
try {
|
|
120
|
+
linkagePrompt = await buildLinkagePrompt({
|
|
121
|
+
config,
|
|
122
|
+
applicationId: promotion?.applicationId ?? applicationId,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (format === "json") {
|
|
129
|
+
process.stdout.write(JSON.stringify(buildJsonOutput(artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt), null, 2) + "\n");
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
renderHumanOutput(artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt);
|
|
133
|
+
}
|
|
134
|
+
return { exitCode: 0 };
|
|
135
|
+
}
|
|
136
|
+
async function handleUnauthenticated(args) {
|
|
137
|
+
const isInteractive = process.stdout.isTTY && process.stdin.isTTY && process.env.CI !== "true";
|
|
138
|
+
if (!isInteractive) {
|
|
139
|
+
printNonTtyAuthInstructions(args.format);
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
if (args.format === "json") {
|
|
143
|
+
process.stdout.write(JSON.stringify({
|
|
144
|
+
schema_version: "1",
|
|
145
|
+
error: "not_authenticated",
|
|
146
|
+
message: "zivis check requires authentication; run `zivis auth login` interactively.",
|
|
147
|
+
}, null, 2) + "\n");
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
const choice = await promptAuthChoice();
|
|
151
|
+
if (choice === "quit") {
|
|
152
|
+
process.stdout.write("\nCancelled — run `zivis check` again when ready.\n");
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
process.stdout.write(`\nOpening your browser to ${choice === "create" ? "sign up" : "sign in"}...\n`);
|
|
156
|
+
try {
|
|
157
|
+
await loginCommand({});
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
process.stdout.write(`\nAuthentication failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
process.stdout.write("\nRunning zivis check ...\n\n");
|
|
164
|
+
return "continue";
|
|
165
|
+
}
|
|
166
|
+
function printNonTtyAuthInstructions(format) {
|
|
167
|
+
if (format === "json") {
|
|
168
|
+
process.stdout.write(JSON.stringify({
|
|
169
|
+
schema_version: "1",
|
|
170
|
+
error: "not_authenticated",
|
|
171
|
+
message: "zivis check requires authentication; set ZIVIS_API_KEY for CI or run interactively.",
|
|
172
|
+
help: {
|
|
173
|
+
sign_up: "https://app.zivis.ai/sign-up?source=cli",
|
|
174
|
+
sign_in: "https://app.zivis.ai/sign-in?source=cli",
|
|
175
|
+
api_keys: "https://app.zivis.ai/settings/api-keys",
|
|
176
|
+
},
|
|
177
|
+
}, null, 2) + "\n");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
process.stdout.write(`\nZIVIS requires an account to inspect repos.\n\n` +
|
|
181
|
+
` → Sign up: https://app.zivis.ai/sign-up?source=cli\n` +
|
|
182
|
+
` → Sign in: https://app.zivis.ai/sign-in?source=cli\n` +
|
|
183
|
+
` → For CI / headless use: set ZIVIS_API_KEY (issue a service key from\n` +
|
|
184
|
+
` https://app.zivis.ai/settings/api-keys)\n\n`);
|
|
185
|
+
}
|
|
186
|
+
async function promptAuthChoice() {
|
|
187
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
188
|
+
try {
|
|
189
|
+
process.stdout.write(`\nZIVIS requires an account to inspect repos.\n\n` +
|
|
190
|
+
` 1) Create account (recommended — first time using ZIVIS)\n` +
|
|
191
|
+
` 2) Sign in (already have an account)\n` +
|
|
192
|
+
` q) Quit\n\n`);
|
|
193
|
+
for (;;) {
|
|
194
|
+
const raw = await new Promise((resolve) => rl.question("> ", resolve));
|
|
195
|
+
const choice = raw.trim().toLowerCase();
|
|
196
|
+
if (choice === "" || choice === "1")
|
|
197
|
+
return "create";
|
|
198
|
+
if (choice === "2")
|
|
199
|
+
return "signin";
|
|
200
|
+
if (choice === "q" || choice === "quit")
|
|
201
|
+
return "quit";
|
|
202
|
+
process.stdout.write("Please choose 1, 2, or q.\n");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
rl.close();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function postCheckArtifact(args) {
|
|
210
|
+
const { config, creds, artifact, findings, repoName } = args;
|
|
211
|
+
const url = `${config.apiBaseUrl}/api/cli/check`;
|
|
212
|
+
const headers = {
|
|
213
|
+
"Content-Type": "application/json",
|
|
214
|
+
"User-Agent": "@zivis/cli",
|
|
215
|
+
};
|
|
216
|
+
if (creds.method === "oauth" && creds.accessToken) {
|
|
217
|
+
headers.Authorization = `Bearer ${creds.accessToken}`;
|
|
218
|
+
}
|
|
219
|
+
else if (creds.method === "apikey" && creds.apiKey) {
|
|
220
|
+
headers["X-API-Key"] = creds.apiKey;
|
|
221
|
+
}
|
|
222
|
+
const body = {
|
|
223
|
+
artifact,
|
|
224
|
+
matches: findings.map((f) => ({
|
|
225
|
+
pattern_id: f.pattern_id,
|
|
226
|
+
lane: f.lane,
|
|
227
|
+
confidence: f.confidence,
|
|
228
|
+
evidence_spans: (f.evidence ?? []).map((e) => ({
|
|
229
|
+
file: e.file,
|
|
230
|
+
line_span: e.end_line ? [e.start_line, e.end_line] : undefined,
|
|
231
|
+
})),
|
|
232
|
+
capsule: {
|
|
233
|
+
safe_summary: f.safe_summary,
|
|
234
|
+
threats: Array.isArray(f.threats) ? f.threats : undefined,
|
|
235
|
+
},
|
|
236
|
+
})),
|
|
237
|
+
repoName,
|
|
238
|
+
};
|
|
239
|
+
const response = await fetch(url, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers,
|
|
242
|
+
body: JSON.stringify(body),
|
|
243
|
+
});
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
const text = await response.text().catch(() => "");
|
|
246
|
+
throw new Error(`HTTP ${response.status} from /api/cli/check: ${text.slice(0, 400)}`);
|
|
247
|
+
}
|
|
248
|
+
return (await response.json());
|
|
249
|
+
}
|
|
250
|
+
function summarizeArtifact(artifact, findings) {
|
|
251
|
+
const services = artifact.nodes.filter((n) => n.type === "service").length;
|
|
252
|
+
const endpoints = artifact.nodes.filter((n) => n.type === "endpoint").length;
|
|
253
|
+
const deps = artifact.nodes.filter((n) => n.type === "external_dependency");
|
|
254
|
+
const llmIntegrations = new Set();
|
|
255
|
+
for (const dep of deps) {
|
|
256
|
+
if (dep.type !== "external_dependency")
|
|
257
|
+
continue;
|
|
258
|
+
const pkg = dep.attributes.package;
|
|
259
|
+
if (!pkg)
|
|
260
|
+
continue;
|
|
261
|
+
if (LLM_PACKAGE_NAMES.has(pkg)) {
|
|
262
|
+
llmIntegrations.add(friendlyLlmName(pkg));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const trustedMatches = findings.filter((f) => f.lane === "trusted").length;
|
|
266
|
+
const inferredMatches = findings.filter((f) => f.lane === "inferred").length;
|
|
267
|
+
const topPatterns = findings
|
|
268
|
+
.slice()
|
|
269
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
270
|
+
.slice(0, 3)
|
|
271
|
+
.map((f) => f.pattern_id);
|
|
272
|
+
return {
|
|
273
|
+
serviceCount: services,
|
|
274
|
+
endpointCount: endpoints,
|
|
275
|
+
externalDependencyCount: deps.length,
|
|
276
|
+
llmIntegrations: Array.from(llmIntegrations).sort(),
|
|
277
|
+
languages: artifact.scope.languages ?? [],
|
|
278
|
+
filesScanned: artifact.scope.files_scanned ?? 0,
|
|
279
|
+
trustedMatches,
|
|
280
|
+
inferredMatches,
|
|
281
|
+
topPatterns,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function friendlyLlmName(pkg) {
|
|
285
|
+
if (pkg === "openai" || pkg === "@azure/openai")
|
|
286
|
+
return "openai";
|
|
287
|
+
if (pkg.startsWith("@anthropic-ai") || pkg === "anthropic")
|
|
288
|
+
return "anthropic";
|
|
289
|
+
if (pkg.startsWith("@google") || pkg.includes("vertexai") || pkg.includes("generative-ai")) {
|
|
290
|
+
return "google";
|
|
291
|
+
}
|
|
292
|
+
if (pkg.includes("bedrock"))
|
|
293
|
+
return "aws-bedrock";
|
|
294
|
+
if (pkg.startsWith("@langchain") || pkg === "langchain")
|
|
295
|
+
return "langchain";
|
|
296
|
+
if (pkg === "llamaindex")
|
|
297
|
+
return "llamaindex";
|
|
298
|
+
if (pkg === "ollama")
|
|
299
|
+
return "ollama";
|
|
300
|
+
if (pkg.includes("cohere"))
|
|
301
|
+
return "cohere";
|
|
302
|
+
if (pkg.includes("mistral"))
|
|
303
|
+
return "mistral";
|
|
304
|
+
if (pkg.includes("groq"))
|
|
305
|
+
return "groq";
|
|
306
|
+
if (pkg === "ai")
|
|
307
|
+
return "vercel-ai";
|
|
308
|
+
return pkg;
|
|
309
|
+
}
|
|
310
|
+
function renderHumanOutput(_artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt) {
|
|
311
|
+
const seconds = (durationMs / 1000).toFixed(1);
|
|
312
|
+
let out = `\nZIVIS read your repo (${seconds}s):\n\n`;
|
|
313
|
+
out += ` Architecture (current snapshot)\n`;
|
|
314
|
+
out += ` ${summary.serviceCount} service${plural(summary.serviceCount)} · ${summary.endpointCount} endpoint${plural(summary.endpointCount)}\n`;
|
|
315
|
+
if (summary.llmIntegrations.length > 0) {
|
|
316
|
+
out += ` ${summary.llmIntegrations.length} LLM integration${plural(summary.llmIntegrations.length)} (${summary.llmIntegrations.join(", ")})\n`;
|
|
317
|
+
}
|
|
318
|
+
if (summary.languages.length > 0) {
|
|
319
|
+
out += ` Languages: ${summary.languages.join(", ")}\n`;
|
|
320
|
+
}
|
|
321
|
+
const totalMatches = summary.trustedMatches + summary.inferredMatches;
|
|
322
|
+
if (totalMatches > 0) {
|
|
323
|
+
out += `\n Patterns matched\n`;
|
|
324
|
+
out += ` ${summary.trustedMatches} trusted · ${summary.inferredMatches} inferred (need IDE review)\n`;
|
|
325
|
+
for (const p of summary.topPatterns) {
|
|
326
|
+
out += ` • ${p}\n`;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (promotion) {
|
|
330
|
+
out += `\n ${promotion.isNewApplication ? "Created" : "Updated"} threat model — "${promotion.applicationName}"\n`;
|
|
331
|
+
out += ` Components: +${promotion.componentsAdded}`;
|
|
332
|
+
if (promotion.componentsUnchanged > 0)
|
|
333
|
+
out += ` (${promotion.componentsUnchanged} already present)`;
|
|
334
|
+
out += `\n`;
|
|
335
|
+
out += ` Data flows: +${promotion.dataFlowsAdded}`;
|
|
336
|
+
if (promotion.dataFlowsUnchanged > 0)
|
|
337
|
+
out += ` (${promotion.dataFlowsUnchanged} already present)`;
|
|
338
|
+
out += `\n`;
|
|
339
|
+
out += ` Actors: +${promotion.actorsAttached}\n`;
|
|
340
|
+
out += ` Observations: +${promotion.findingsCreated}\n`;
|
|
341
|
+
out += ` Scenarios: +${promotion.scenariosGenerated}\n`;
|
|
342
|
+
out += `\n View it: ${promotion.webAppUrl}\n`;
|
|
343
|
+
}
|
|
344
|
+
else if (network && promotionError) {
|
|
345
|
+
out += `\n Could not post to your threat model: ${promotionError}\n`;
|
|
346
|
+
out += ` (graph + matches still produced locally; rerun once the API is reachable)\n`;
|
|
347
|
+
}
|
|
348
|
+
else if (!network) {
|
|
349
|
+
out += `\n (--no-network: nothing posted to your threat model)\n`;
|
|
350
|
+
}
|
|
351
|
+
out += `\nContinue:\n`;
|
|
352
|
+
out += ` → In your IDE: zivis mcp install --for claude-code\n`;
|
|
353
|
+
out += ` → Human-reviewed Mini-Threat-Model: zivis teardown ($500)\n`;
|
|
354
|
+
out += ` → Full Deal-Readiness Threat Model: zivis sales ($7.5K–$15K)\n`;
|
|
355
|
+
if (linkagePrompt) {
|
|
356
|
+
out += linkagePrompt + "\n";
|
|
357
|
+
}
|
|
358
|
+
out += "\n";
|
|
359
|
+
process.stdout.write(out);
|
|
360
|
+
}
|
|
361
|
+
function plural(n) {
|
|
362
|
+
return n === 1 ? "" : "s";
|
|
363
|
+
}
|
|
364
|
+
function buildJsonOutput(artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt) {
|
|
365
|
+
let promotionField;
|
|
366
|
+
if (promotion)
|
|
367
|
+
promotionField = { status: "ok", ...promotion };
|
|
368
|
+
else if (!network)
|
|
369
|
+
promotionField = { status: "skipped", reason: "no-network" };
|
|
370
|
+
else if (promotionError)
|
|
371
|
+
promotionField = { status: "error", error: promotionError };
|
|
372
|
+
return {
|
|
373
|
+
schema_version: "1",
|
|
374
|
+
repo_id: artifact.provenance.repo_id ?? "unknown",
|
|
375
|
+
ran_at: artifact.extracted_at,
|
|
376
|
+
duration_ms: durationMs,
|
|
377
|
+
architecture: {
|
|
378
|
+
services: summary.serviceCount,
|
|
379
|
+
endpoints: summary.endpointCount,
|
|
380
|
+
external_dependencies: summary.externalDependencyCount,
|
|
381
|
+
llm_integrations: summary.llmIntegrations,
|
|
382
|
+
languages: summary.languages,
|
|
383
|
+
},
|
|
384
|
+
matches: {
|
|
385
|
+
trusted: summary.trustedMatches,
|
|
386
|
+
inferred: summary.inferredMatches,
|
|
387
|
+
top_patterns: summary.topPatterns,
|
|
388
|
+
},
|
|
389
|
+
promotion: promotionField,
|
|
390
|
+
conversion_links: {
|
|
391
|
+
mcp_install: "zivis mcp install --for <ide>",
|
|
392
|
+
teardown: "zivis teardown",
|
|
393
|
+
sales: "zivis sales",
|
|
394
|
+
},
|
|
395
|
+
linkage_prompt: linkagePrompt,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function printIfHuman(format, msg) {
|
|
399
|
+
if (format === "human")
|
|
400
|
+
process.stdout.write(msg + "\n");
|
|
401
|
+
}
|
|
402
|
+
async function readGitMetadata(rootDir) {
|
|
403
|
+
return {
|
|
404
|
+
remoteUrl: await tryGit(rootDir, "remote get-url origin"),
|
|
405
|
+
commitSha: await tryGit(rootDir, "rev-parse HEAD"),
|
|
406
|
+
branch: await tryGit(rootDir, "rev-parse --abbrev-ref HEAD"),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
async function tryGit(cwd, args) {
|
|
410
|
+
try {
|
|
411
|
+
const { stdout } = await execAsync(`git ${args}`, { cwd });
|
|
412
|
+
const value = stdout.trim();
|
|
413
|
+
return value || undefined;
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
return undefined;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function deriveRepoId(remoteUrl, rootDir) {
|
|
420
|
+
if (remoteUrl) {
|
|
421
|
+
return "remote-" + sha256Short(remoteUrl);
|
|
422
|
+
}
|
|
423
|
+
return "path-" + sha256Short(rootDir);
|
|
424
|
+
}
|
|
425
|
+
function sha256Short(value) {
|
|
426
|
+
return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
427
|
+
}
|
|
428
|
+
function deriveRepoNameFromGit(remoteUrl, rootDir) {
|
|
429
|
+
if (remoteUrl) {
|
|
430
|
+
const m = /[/:]([^/:]+?)\/([^/]+?)(?:\.git)?$/.exec(remoteUrl);
|
|
431
|
+
if (m)
|
|
432
|
+
return m[2];
|
|
433
|
+
}
|
|
434
|
+
const base = path.basename(rootDir);
|
|
435
|
+
return base && base !== "." ? base : undefined;
|
|
436
|
+
}
|
|
437
|
+
function userCacheRoot() {
|
|
438
|
+
const home = os.homedir();
|
|
439
|
+
if (process.platform === "darwin") {
|
|
440
|
+
return path.join(home, "Library", "Caches", "zivis");
|
|
441
|
+
}
|
|
442
|
+
if (process.platform === "win32") {
|
|
443
|
+
const localAppData = process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local");
|
|
444
|
+
return path.join(localAppData, "zivis", "Cache");
|
|
445
|
+
}
|
|
446
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
447
|
+
return path.join(xdg ?? path.join(home, ".cache"), "zivis");
|
|
448
|
+
}
|
|
449
|
+
async function writeArtifactToCache(artifact) {
|
|
450
|
+
const repoId = artifact.provenance.repo_id ?? "unknown";
|
|
451
|
+
const commitSha = artifact.provenance.commit_sha;
|
|
452
|
+
const runId = formatRunId(commitSha, artifact.extracted_at);
|
|
453
|
+
const outPath = path.join(userCacheRoot(), "repos", repoId, "inspect", "runs", `${runId}.json`);
|
|
454
|
+
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
455
|
+
await fs.writeFile(outPath, JSON.stringify(artifact, null, 2) + "\n");
|
|
456
|
+
return outPath;
|
|
457
|
+
}
|
|
458
|
+
function formatRunId(commitSha, extractedAt) {
|
|
459
|
+
const compactTs = extractedAt.replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
460
|
+
if (!commitSha)
|
|
461
|
+
return `nogit-${compactTs}`;
|
|
462
|
+
return `${commitSha.slice(0, 7)}-${compactTs}`;
|
|
463
|
+
}
|
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `zivis inspect` — local repo graph extraction.
|
|
3
|
-
*
|
|
4
|
-
* Walks the repo with the existing extractor, converts the result into a
|
|
5
|
-
* RepoGraphArtifactV1 (the canonical contract), and writes it to the
|
|
6
|
-
* user-level cache. This is the user-facing entrypoint for Phase 1 of the
|
|
7
|
-
* local repo graph + pattern library work.
|
|
8
|
-
*
|
|
9
|
-
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
|
|
10
|
-
* Schema: schemas/repo-graph-artifact.schema.json
|
|
11
|
-
*
|
|
12
|
-
* The command is local-only and never makes network calls. Persistence to
|
|
13
|
-
* the platform happens in a separate step (Phase 4 — `/inspect` upload),
|
|
14
|
-
* gated on Application mapping resolution per plan §Repo->Application Mapping.
|
|
15
|
-
*/
|
|
16
1
|
import type { Command } from "commander";
|
|
17
2
|
export declare function registerInspectCommands(program: Command): void;
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `zivis inspect` — local repo graph extraction.
|
|
3
|
-
*
|
|
4
|
-
* Walks the repo with the existing extractor, converts the result into a
|
|
5
|
-
* RepoGraphArtifactV1 (the canonical contract), and writes it to the
|
|
6
|
-
* user-level cache. This is the user-facing entrypoint for Phase 1 of the
|
|
7
|
-
* local repo graph + pattern library work.
|
|
8
|
-
*
|
|
9
|
-
* Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
|
|
10
|
-
* Schema: schemas/repo-graph-artifact.schema.json
|
|
11
|
-
*
|
|
12
|
-
* The command is local-only and never makes network calls. Persistence to
|
|
13
|
-
* the platform happens in a separate step (Phase 4 — `/inspect` upload),
|
|
14
|
-
* gated on Application mapping resolution per plan §Repo->Application Mapping.
|
|
15
|
-
*/
|
|
16
1
|
import * as crypto from "node:crypto";
|
|
17
2
|
import * as fs from "node:fs/promises";
|
|
18
3
|
import * as os from "node:os";
|
|
@@ -115,12 +100,6 @@ function deriveRepoId(remoteUrl, rootDir) {
|
|
|
115
100
|
function sha256Short(value) {
|
|
116
101
|
return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
117
102
|
}
|
|
118
|
-
/**
|
|
119
|
-
* XDG-compliant user cache root. Layout follows the plan §2A:
|
|
120
|
-
* macOS: ~/Library/Caches/zivis
|
|
121
|
-
* Linux: $XDG_CACHE_HOME/zivis (or ~/.cache/zivis)
|
|
122
|
-
* Windows: %LOCALAPPDATA%\zivis\Cache
|
|
123
|
-
*/
|
|
124
103
|
function userCacheRoot() {
|
|
125
104
|
const home = os.homedir();
|
|
126
105
|
if (process.platform === "darwin") {
|
|
@@ -138,7 +117,6 @@ function defaultCacheOutPath(repoId, commitSha, extractedAt) {
|
|
|
138
117
|
return path.join(userCacheRoot(), "repos", repoId, "inspect", "runs", `${runId}.json`);
|
|
139
118
|
}
|
|
140
119
|
function formatRunId(commitSha, extractedAt) {
|
|
141
|
-
// <short-commit>-<compact-iso> e.g. abc1234-20260505T201600Z
|
|
142
120
|
const compactTs = extractedAt.replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
143
121
|
if (!commitSha)
|
|
144
122
|
return `nogit-${compactTs}`;
|
|
@@ -156,4 +134,3 @@ function printSummary(artifact, suffix) {
|
|
|
156
134
|
` ${artifact.edges.length} edges (${exposes} exposes, ${dependsOn} depends_on)\n` +
|
|
157
135
|
(suffix ? ` ${suffix}\n` : ""));
|
|
158
136
|
}
|
|
159
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -12,10 +12,6 @@ const KNOWN_CLIENTS = [
|
|
|
12
12
|
"vscode-copilot",
|
|
13
13
|
"vscode-claude",
|
|
14
14
|
];
|
|
15
|
-
/**
|
|
16
|
-
* Map the CLI --client value to the GuidanceClient type used by writeGuidance().
|
|
17
|
-
* "claude-code" (manual) has no guidance files — skip it.
|
|
18
|
-
*/
|
|
19
15
|
function toGuidanceClient(client) {
|
|
20
16
|
switch (client) {
|
|
21
17
|
case "cursor": return "cursor";
|
|
@@ -97,23 +93,19 @@ export function registerMcpCommands(program) {
|
|
|
97
93
|
console.error("Install failed:", err instanceof Error ? err.message : err);
|
|
98
94
|
process.exit(1);
|
|
99
95
|
}
|
|
100
|
-
// "claude-code" is manual — no guidance files to write
|
|
101
96
|
if (client === "claude-code")
|
|
102
97
|
return;
|
|
103
98
|
const guidanceClient = toGuidanceClient(client);
|
|
104
99
|
if (!guidanceClient)
|
|
105
100
|
return;
|
|
106
|
-
// Determine whether to write guidance files
|
|
107
101
|
let shouldWrite;
|
|
108
102
|
if (opts.withGuidance) {
|
|
109
103
|
shouldWrite = true;
|
|
110
104
|
}
|
|
111
105
|
else if (opts.guidance === false) {
|
|
112
|
-
// Commander sets opts.guidance = false when --no-guidance is passed
|
|
113
106
|
shouldWrite = false;
|
|
114
107
|
}
|
|
115
108
|
else {
|
|
116
|
-
// Interactive default
|
|
117
109
|
console.log("");
|
|
118
110
|
shouldWrite = await promptYesNo("Write ZIVIS guidance files so your IDE knows when to suggest security checks?\n" +
|
|
119
111
|
"(Adds a rule/instruction file to the project. You can edit or remove it anytime.) [Y/n] ");
|
|
@@ -133,4 +125,3 @@ export function registerMcpCommands(program) {
|
|
|
133
125
|
}
|
|
134
126
|
});
|
|
135
127
|
}
|
|
136
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `zivis open` — Build and (by default) launch a vscode:// or cursor:// deep link
|
|
3
|
-
* that opens a ZIVIS artifact in the ZIVIS Code IDE extension. Use `--ide cursor`
|
|
4
|
-
* for Cursor. With --print, emit the URI to stdout for piping/sharing.
|
|
5
|
-
*
|
|
6
|
-
* Mirrors the URI shape emitted by the `zivis_open_in_ide` MCP tool and the
|
|
7
|
-
* web app's buildIdeLink() helper. See docs/platform/integrations/IDE-DEEP-LINKS.md.
|
|
8
|
-
*/
|
|
9
1
|
import type { Command } from "commander";
|
|
10
2
|
export declare function registerOpenCommands(program: Command): void;
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `zivis open` — Build and (by default) launch a vscode:// or cursor:// deep link
|
|
3
|
-
* that opens a ZIVIS artifact in the ZIVIS Code IDE extension. Use `--ide cursor`
|
|
4
|
-
* for Cursor. With --print, emit the URI to stdout for piping/sharing.
|
|
5
|
-
*
|
|
6
|
-
* Mirrors the URI shape emitted by the `zivis_open_in_ide` MCP tool and the
|
|
7
|
-
* web app's buildIdeLink() helper. See docs/platform/integrations/IDE-DEEP-LINKS.md.
|
|
8
|
-
*/
|
|
9
1
|
const KINDS = ["diagram", "document", "threat-model"];
|
|
10
2
|
export function registerOpenCommands(program) {
|
|
11
3
|
program
|
|
@@ -47,11 +39,8 @@ export function registerOpenCommands(program) {
|
|
|
47
39
|
console.log(uri);
|
|
48
40
|
return;
|
|
49
41
|
}
|
|
50
|
-
// Lazy-import `open` so --print stays dependency-free in environments
|
|
51
|
-
// (e.g. headless CI) where launching a URL handler would fail or hang.
|
|
52
42
|
const { default: open } = await import("open");
|
|
53
43
|
await open(uri);
|
|
54
44
|
console.log(uri);
|
|
55
45
|
});
|
|
56
46
|
}
|
|
57
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -118,11 +118,6 @@ export function registerScanCommands(program) {
|
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
|
-
/**
|
|
122
|
-
* Map customer-facing ApplicationType to the RT scan's targetType enum.
|
|
123
|
-
* The two enums differ historically: ApplicationType is product-shape,
|
|
124
|
-
* targetType is what the RT runner needs to know about the wire format.
|
|
125
|
-
*/
|
|
126
121
|
function appTypeToTargetType(appType) {
|
|
127
122
|
switch (appType) {
|
|
128
123
|
case "agent":
|
|
@@ -137,4 +132,3 @@ function appTypeToTargetType(appType) {
|
|
|
137
132
|
return undefined;
|
|
138
133
|
}
|
|
139
134
|
}
|
|
140
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -4,8 +4,6 @@ import { getStorageName } from "../../internal/target-auth/keychain.js";
|
|
|
4
4
|
import { decodeJwtPayload } from "../../internal/target-auth/workos.js";
|
|
5
5
|
const DEFAULT_PERSONA = "default";
|
|
6
6
|
function warnDeprecated() {
|
|
7
|
-
// stderr so it doesn't pollute stdout (CLI scripts pipe `zivis target token`
|
|
8
|
-
// straight into curl). One line, no color codes — keeps grep-friendly.
|
|
9
7
|
console.error("[zivis] WARNING: 'zivis target' is deprecated; use 'zivis app' instead.");
|
|
10
8
|
}
|
|
11
9
|
export function registerTargetCommands(program) {
|
|
@@ -143,4 +141,3 @@ export function registerTargetCommands(program) {
|
|
|
143
141
|
console.log(`✓ Cleared tokens for target ${targetId} (persona: ${opts.persona})`);
|
|
144
142
|
});
|
|
145
143
|
}
|
|
146
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -14,7 +14,6 @@ function collect(value, previous) {
|
|
|
14
14
|
return previous.concat([value]);
|
|
15
15
|
}
|
|
16
16
|
function defaultOutPath(extractedAt) {
|
|
17
|
-
// .zivis/extracted/<iso-no-colons>.json — matches the doc's example.
|
|
18
17
|
const ts = extractedAt.replace(/[:.]/g, "-");
|
|
19
18
|
return `.zivis/extracted/${ts}.json`;
|
|
20
19
|
}
|
|
@@ -80,9 +79,6 @@ export function registerTmCommands(program) {
|
|
|
80
79
|
process.exit(1);
|
|
81
80
|
});
|
|
82
81
|
const parsed = JSON.parse(raw);
|
|
83
|
-
// Only fields the PATCH endpoint accepts. tm pull dumps the full
|
|
84
|
-
// record; tm push only sends the editable subset, so a round-trip
|
|
85
|
-
// edit cycle Just Works.
|
|
86
82
|
const payload = {};
|
|
87
83
|
for (const k of ["name", "description", "status", "campaignId"]) {
|
|
88
84
|
if (parsed[k] !== undefined)
|
|
@@ -123,8 +119,6 @@ export function registerTmCommands(program) {
|
|
|
123
119
|
extractorVersion: cliPkg.version,
|
|
124
120
|
});
|
|
125
121
|
if (opts.privacyAudit) {
|
|
126
|
-
// Prints the entire object that WOULD be sent to ZIVIS via --apply.
|
|
127
|
-
// Never makes a network call; the extractor itself is local-only.
|
|
128
122
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
129
123
|
process.stderr.write(`\n[zivis] Privacy audit: ${result.scope.filesScanned} files scanned, ` +
|
|
130
124
|
`${result.components.length} component(s), ${result.externalServices.length} external service(s), ` +
|
|
@@ -263,4 +257,3 @@ export function registerTmCommands(program) {
|
|
|
263
257
|
}
|
|
264
258
|
});
|
|
265
259
|
}
|
|
266
|
-
//# sourceMappingURL=index.js.map
|