@quint-security/cli 0.1.2
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/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +87 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/http-proxy.d.ts +3 -0
- package/dist/commands/http-proxy.d.ts.map +1 -0
- package/dist/commands/http-proxy.js +35 -0
- package/dist/commands/http-proxy.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +343 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/keys.d.ts +3 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +58 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +50 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/policy.d.ts +3 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +39 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/proxy.d.ts +3 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +24 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +46 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +104 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/package.json +27 -0
- package/src/commands/auth.ts +103 -0
- package/src/commands/http-proxy.ts +34 -0
- package/src/commands/init.ts +408 -0
- package/src/commands/keys.ts +71 -0
- package/src/commands/logs.ts +59 -0
- package/src/commands/policy.ts +39 -0
- package/src/commands/proxy.ts +22 -0
- package/src/commands/status.ts +51 -0
- package/src/commands/verify.ts +117 -0
- package/src/index.ts +29 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logsCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const core_1 = require("@quint-security/core");
|
|
6
|
+
exports.logsCommand = new commander_1.Command("logs")
|
|
7
|
+
.description("Search and display the audit log")
|
|
8
|
+
.option("--server <name>", "Filter by server name")
|
|
9
|
+
.option("--tool <name>", "Filter by tool name")
|
|
10
|
+
.option("--denied", "Show only denied entries")
|
|
11
|
+
.option("--since <iso-date>", "Show entries since ISO-8601 date")
|
|
12
|
+
.option("-n, --limit <count>", "Max entries to show", "50")
|
|
13
|
+
.option("--json", "Output as JSON")
|
|
14
|
+
.action((opts) => {
|
|
15
|
+
const policy = (0, core_1.loadPolicy)();
|
|
16
|
+
const dataDir = (0, core_1.resolveDataDir)(policy.data_dir);
|
|
17
|
+
const db = (0, core_1.openAuditDb)(dataDir);
|
|
18
|
+
try {
|
|
19
|
+
const entries = db.query({
|
|
20
|
+
server: opts.server,
|
|
21
|
+
tool: opts.tool,
|
|
22
|
+
verdict: opts.denied ? "deny" : undefined,
|
|
23
|
+
since: opts.since,
|
|
24
|
+
limit: parseInt(opts.limit, 10),
|
|
25
|
+
});
|
|
26
|
+
if (opts.json) {
|
|
27
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (entries.length === 0) {
|
|
31
|
+
console.log("No audit log entries found.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log(`Showing ${entries.length} entries (newest first):\n`);
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
printEntry(entry);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
db.close();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
function printEntry(e) {
|
|
44
|
+
const icon = e.verdict === "deny" ? "✗" : e.verdict === "allow" ? "✓" : "→";
|
|
45
|
+
const ts = e.timestamp.replace("T", " ").replace(/\.\d+Z$/, "Z");
|
|
46
|
+
const tool = e.tool_name ? ` tool=${e.tool_name}` : "";
|
|
47
|
+
const risk = e.risk_score != null ? ` risk=${e.risk_score}(${e.risk_level})` : "";
|
|
48
|
+
console.log(` ${icon} [${ts}] ${e.direction} ${e.method}${tool} server=${e.server_name} verdict=${e.verdict}${risk} id=${e.id}`);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/commands/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,aAAa,SACoB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.policyCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const core_1 = require("@quint-security/core");
|
|
6
|
+
exports.policyCommand = new commander_1.Command("policy")
|
|
7
|
+
.description("Manage access control policy");
|
|
8
|
+
exports.policyCommand
|
|
9
|
+
.command("init")
|
|
10
|
+
.description("Create a default policy.json if none exists")
|
|
11
|
+
.action(() => {
|
|
12
|
+
const path = (0, core_1.initPolicy)();
|
|
13
|
+
console.log(`Policy file: ${path}`);
|
|
14
|
+
});
|
|
15
|
+
exports.policyCommand
|
|
16
|
+
.command("validate")
|
|
17
|
+
.description("Validate the current policy.json")
|
|
18
|
+
.action(() => {
|
|
19
|
+
const policy = (0, core_1.loadPolicy)();
|
|
20
|
+
const errors = (0, core_1.validatePolicy)(policy);
|
|
21
|
+
if (errors.length === 0) {
|
|
22
|
+
console.log("Policy is valid.");
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
console.log("Policy validation errors:");
|
|
26
|
+
for (const err of errors) {
|
|
27
|
+
console.log(` - ${err}`);
|
|
28
|
+
}
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
exports.policyCommand
|
|
33
|
+
.command("show")
|
|
34
|
+
.description("Display the current policy")
|
|
35
|
+
.action(() => {
|
|
36
|
+
const policy = (0, core_1.loadPolicy)();
|
|
37
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/commands/policy.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AACpC,+CAA8F;AAEjF,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,8BAA8B,CAAC,CAAC;AAE/C,qBAAa;KACV,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,IAAI,GAAG,IAAA,iBAAU,GAAE,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEL,qBAAa;KACV,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,MAAM,GAAG,IAAA,iBAAU,GAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAA,qBAAc,EAAC,MAAM,CAAC,CAAC;IAEtC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,qBAAa;KACV,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,MAAM,GAAG,IAAA,iBAAU,GAAE,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/commands/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,YAAY,SAiBrB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.proxyCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const core_1 = require("@quint-security/core");
|
|
6
|
+
const proxy_1 = require("@quint-security/proxy");
|
|
7
|
+
exports.proxyCommand = new commander_1.Command("proxy")
|
|
8
|
+
.description("Run as MCP stdio proxy wrapping another MCP server")
|
|
9
|
+
.requiredOption("--name <name>", "Name identifier for the proxied server")
|
|
10
|
+
.option("--policy <path>", "Path to policy.json (default: ~/.quint/policy.json)")
|
|
11
|
+
.argument("<command>", "Command to spawn the real MCP server")
|
|
12
|
+
.argument("[args...]", "Arguments for the real MCP server")
|
|
13
|
+
.allowExcessArguments(true)
|
|
14
|
+
.action((command, args, opts) => {
|
|
15
|
+
const policy = (0, core_1.loadPolicy)(opts.policy ? opts.policy : undefined);
|
|
16
|
+
const dataDir = (0, core_1.resolveDataDir)(policy.data_dir);
|
|
17
|
+
(0, proxy_1.startProxy)({
|
|
18
|
+
serverName: opts.name,
|
|
19
|
+
command,
|
|
20
|
+
args,
|
|
21
|
+
policy,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/commands/proxy.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AACpC,+CAAkE;AAClE,iDAAmD;AAEtC,QAAA,YAAY,GAAG,IAAI,mBAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,oDAAoD,CAAC;KACjE,cAAc,CAAC,eAAe,EAAE,wCAAwC,CAAC;KACzE,MAAM,CAAC,iBAAiB,EAAE,qDAAqD,CAAC;KAChF,QAAQ,CAAC,WAAW,EAAE,sCAAsC,CAAC;KAC7D,QAAQ,CAAC,WAAW,EAAE,mCAAmC,CAAC;KAC1D,oBAAoB,CAAC,IAAI,CAAC;KAC1B,MAAM,CAAC,CAAC,OAAe,EAAE,IAAc,EAAE,IAAuC,EAAE,EAAE;IACnF,MAAM,MAAM,GAAG,IAAA,iBAAU,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,IAAA,qBAAc,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEhD,IAAA,kBAAU,EAAC;QACT,UAAU,EAAE,IAAI,CAAC,IAAI;QACrB,OAAO;QACP,IAAI;QACJ,MAAM;KACP,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,aAAa,SAuCtB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.statusCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const core_1 = require("@quint-security/core");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
exports.statusCommand = new commander_1.Command("status")
|
|
9
|
+
.description("Show Quint configuration summary")
|
|
10
|
+
.action(() => {
|
|
11
|
+
const policy = (0, core_1.loadPolicy)();
|
|
12
|
+
const dataDir = (0, core_1.resolveDataDir)(policy.data_dir);
|
|
13
|
+
console.log("Quint Status");
|
|
14
|
+
console.log("============");
|
|
15
|
+
console.log(` Data dir: ${dataDir}`);
|
|
16
|
+
console.log(` Policy: ${(0, node_path_1.join)(dataDir, "policy.json")} ${(0, node_fs_1.existsSync)((0, node_path_1.join)(dataDir, "policy.json")) ? "(found)" : "(not found)"}`);
|
|
17
|
+
console.log(` Log level: ${policy.log_level}`);
|
|
18
|
+
// Keys
|
|
19
|
+
const kp = (0, core_1.loadKeyPair)(dataDir);
|
|
20
|
+
if (kp) {
|
|
21
|
+
console.log(` Keys: ${(0, core_1.publicKeyFingerprint)(kp.publicKey)} (loaded)`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log(` Keys: not generated (run \`quint keys generate\`)`);
|
|
25
|
+
}
|
|
26
|
+
// Database
|
|
27
|
+
const dbPath = (0, node_path_1.join)(dataDir, "quint.db");
|
|
28
|
+
if ((0, node_fs_1.existsSync)(dbPath)) {
|
|
29
|
+
const db = (0, core_1.openAuditDb)(dataDir);
|
|
30
|
+
const count = db.count();
|
|
31
|
+
db.close();
|
|
32
|
+
console.log(` Database: ${dbPath} (${count} entries)`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log(` Database: not created yet`);
|
|
36
|
+
}
|
|
37
|
+
// Servers
|
|
38
|
+
console.log(`\n Servers (${policy.servers.length}):`);
|
|
39
|
+
for (const srv of policy.servers) {
|
|
40
|
+
const toolRules = srv.tools.length > 0
|
|
41
|
+
? srv.tools.map(t => `${t.tool}:${t.action}`).join(", ")
|
|
42
|
+
: "no tool-specific rules";
|
|
43
|
+
console.log(` ${srv.server} → default:${srv.default_action} [${toolRules}]`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AACpC,+CAM8B;AAC9B,qCAAqC;AACrC,yCAAiC;AAEpB,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,MAAM,GAAG,IAAA,iBAAU,GAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAA,qBAAc,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEhD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAA,gBAAI,EAAC,OAAO,EAAE,aAAa,CAAC,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;IACtI,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAElD,OAAO;IACP,MAAM,EAAE,GAAG,IAAA,kBAAW,EAAC,OAAO,CAAC,CAAC;IAChC,IAAI,EAAE,EAAE,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAA,2BAAoB,EAAC,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAC;IAC5E,CAAC;IAED,WAAW;IACX,MAAM,MAAM,GAAG,IAAA,gBAAI,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACzC,IAAI,IAAA,oBAAU,EAAC,MAAM,CAAC,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAA,kBAAW,EAAC,OAAO,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;QACzB,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,KAAK,KAAK,WAAW,CAAC,CAAC;IAC7D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAChD,CAAC;IAED,UAAU;IACV,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IACvD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;YACpC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACxD,CAAC,CAAC,wBAAwB,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,MAAM,cAAc,GAAG,CAAC,cAAc,KAAK,SAAS,GAAG,CAAC,CAAC;IAClF,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,aAAa,SAkFtB,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const core_1 = require("@quint-security/core");
|
|
6
|
+
exports.verifyCommand = new commander_1.Command("verify")
|
|
7
|
+
.description("Verify Ed25519 signatures and hash chain on audit log entries")
|
|
8
|
+
.option("--id <n>", "Verify a specific entry by ID")
|
|
9
|
+
.option("--last <n>", "Verify the last N entries", "20")
|
|
10
|
+
.option("--all", "Verify all entries")
|
|
11
|
+
.option("--chain", "Also verify hash chain integrity (requires --all or --last)")
|
|
12
|
+
.action((opts) => {
|
|
13
|
+
const policy = (0, core_1.loadPolicy)();
|
|
14
|
+
const dataDir = (0, core_1.resolveDataDir)(policy.data_dir);
|
|
15
|
+
const db = (0, core_1.openAuditDb)(dataDir);
|
|
16
|
+
try {
|
|
17
|
+
let entries;
|
|
18
|
+
if (opts.id) {
|
|
19
|
+
const entry = db.getById(parseInt(opts.id, 10));
|
|
20
|
+
entries = entry ? [entry] : [];
|
|
21
|
+
}
|
|
22
|
+
else if (opts.all) {
|
|
23
|
+
entries = db.getAll(); // ascending order for chain verification
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
entries = db.getLast(parseInt(opts.last, 10));
|
|
27
|
+
// Reverse to ascending for chain verification
|
|
28
|
+
entries.reverse();
|
|
29
|
+
}
|
|
30
|
+
if (entries.length === 0) {
|
|
31
|
+
console.log("No entries to verify.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Signature verification
|
|
35
|
+
let sigValid = 0;
|
|
36
|
+
let sigInvalid = 0;
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const ok = verifyEntry(entry);
|
|
39
|
+
if (ok) {
|
|
40
|
+
sigValid++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
sigInvalid++;
|
|
44
|
+
console.log(` ✗ INVALID signature on entry #${entry.id} (${entry.timestamp})`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log(`\nSignatures: ${entries.length} checked, ${sigValid} valid, ${sigInvalid} invalid`);
|
|
48
|
+
// Hash chain verification
|
|
49
|
+
if (opts.chain || opts.all) {
|
|
50
|
+
let chainValid = 0;
|
|
51
|
+
let chainBroken = 0;
|
|
52
|
+
for (let i = 1; i < entries.length; i++) {
|
|
53
|
+
const prev = entries[i - 1];
|
|
54
|
+
const curr = entries[i];
|
|
55
|
+
if (curr.prev_hash === "" && prev.prev_hash === "") {
|
|
56
|
+
// Legacy entries without hash chain — skip
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const expectedHash = (0, core_1.sha256)(prev.signature);
|
|
60
|
+
if (curr.prev_hash === expectedHash) {
|
|
61
|
+
chainValid++;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
chainBroken++;
|
|
65
|
+
console.log(` ⛓ BROKEN chain at entry #${curr.id} — prev_hash doesn't match entry #${prev.id}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (chainValid + chainBroken > 0) {
|
|
69
|
+
console.log(`Chain: ${chainValid + chainBroken} links checked, ${chainValid} valid, ${chainBroken} broken`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log(`Chain: no chain data (legacy entries)`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (sigInvalid > 0) {
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
db.close();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
function verifyEntry(entry) {
|
|
84
|
+
const signable = {
|
|
85
|
+
timestamp: entry.timestamp,
|
|
86
|
+
server_name: entry.server_name,
|
|
87
|
+
direction: entry.direction,
|
|
88
|
+
method: entry.method,
|
|
89
|
+
message_id: entry.message_id,
|
|
90
|
+
tool_name: entry.tool_name,
|
|
91
|
+
arguments_json: entry.arguments_json,
|
|
92
|
+
response_json: entry.response_json,
|
|
93
|
+
verdict: entry.verdict,
|
|
94
|
+
risk_score: entry.risk_score ?? null,
|
|
95
|
+
risk_level: entry.risk_level ?? null,
|
|
96
|
+
policy_hash: entry.policy_hash ?? "",
|
|
97
|
+
prev_hash: entry.prev_hash ?? "",
|
|
98
|
+
nonce: entry.nonce ?? "",
|
|
99
|
+
public_key: entry.public_key,
|
|
100
|
+
};
|
|
101
|
+
const canonical = (0, core_1.canonicalize)(signable);
|
|
102
|
+
return (0, core_1.verifySignature)(canonical, entry.signature, entry.public_key);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const proxy_js_1 = require("./commands/proxy.js");
|
|
6
|
+
const http_proxy_js_1 = require("./commands/http-proxy.js");
|
|
7
|
+
const logs_js_1 = require("./commands/logs.js");
|
|
8
|
+
const keys_js_1 = require("./commands/keys.js");
|
|
9
|
+
const verify_js_1 = require("./commands/verify.js");
|
|
10
|
+
const policy_js_1 = require("./commands/policy.js");
|
|
11
|
+
const status_js_1 = require("./commands/status.js");
|
|
12
|
+
const auth_js_1 = require("./commands/auth.js");
|
|
13
|
+
const init_js_1 = require("./commands/init.js");
|
|
14
|
+
const program = new commander_1.Command()
|
|
15
|
+
.name("quint")
|
|
16
|
+
.version("0.1.0")
|
|
17
|
+
.description("Local security proxy for MCP tool calls");
|
|
18
|
+
program.addCommand(init_js_1.initCommand);
|
|
19
|
+
program.addCommand(proxy_js_1.proxyCommand);
|
|
20
|
+
program.addCommand(http_proxy_js_1.httpProxyCommand);
|
|
21
|
+
program.addCommand(logs_js_1.logsCommand);
|
|
22
|
+
program.addCommand(keys_js_1.keysCommand);
|
|
23
|
+
program.addCommand(verify_js_1.verifyCommand);
|
|
24
|
+
program.addCommand(policy_js_1.policyCommand);
|
|
25
|
+
program.addCommand(status_js_1.statusCommand);
|
|
26
|
+
program.addCommand(auth_js_1.authCommand);
|
|
27
|
+
program.parse();
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,kDAAmD;AACnD,4DAA4D;AAC5D,gDAAiD;AACjD,gDAAiD;AACjD,oDAAqD;AACrD,oDAAqD;AACrD,oDAAqD;AACrD,gDAAiD;AACjD,gDAAiD;AAEjD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE;KAC1B,IAAI,CAAC,OAAO,CAAC;KACb,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,yCAAyC,CAAC,CAAC;AAE1D,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,gCAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAEhC,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quint-security/cli",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"bin": {
|
|
5
|
+
"quint": "dist/index.js"
|
|
6
|
+
},
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Quint-Security/cli.git",
|
|
10
|
+
"directory": "packages/cli"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@quint-security/core": "*",
|
|
20
|
+
"@quint-security/proxy": "*",
|
|
21
|
+
"commander": "^12.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"typescript": "^5.4.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
loadPolicy,
|
|
4
|
+
resolveDataDir,
|
|
5
|
+
openAuthDb,
|
|
6
|
+
generateApiKey,
|
|
7
|
+
} from "@quint-security/core";
|
|
8
|
+
|
|
9
|
+
export const authCommand = new Command("auth")
|
|
10
|
+
.description("Manage authentication (API keys and sessions)");
|
|
11
|
+
|
|
12
|
+
authCommand
|
|
13
|
+
.command("create-key")
|
|
14
|
+
.description("Create a new API key")
|
|
15
|
+
.requiredOption("--label <name>", "Human-readable label for the key")
|
|
16
|
+
.option("--scopes <scopes>", "Comma-separated scopes (e.g. proxy:read,audit:write)")
|
|
17
|
+
.option("--ttl <seconds>", "Time-to-live in seconds (0 = no expiry)", "0")
|
|
18
|
+
.action((opts: { label: string; scopes?: string; ttl: string }) => {
|
|
19
|
+
const policy = loadPolicy();
|
|
20
|
+
const dataDir = resolveDataDir(policy.data_dir);
|
|
21
|
+
const db = openAuthDb(dataDir);
|
|
22
|
+
|
|
23
|
+
const scopes = opts.scopes ? opts.scopes.split(",").map((s) => s.trim()) : [];
|
|
24
|
+
const ttl = parseInt(opts.ttl, 10);
|
|
25
|
+
|
|
26
|
+
const { rawKey, apiKey } = generateApiKey(db, {
|
|
27
|
+
label: opts.label,
|
|
28
|
+
scopes,
|
|
29
|
+
ttlSeconds: ttl > 0 ? ttl : undefined,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.log(`API key created.\n`);
|
|
33
|
+
console.log(` ID: ${apiKey.id}`);
|
|
34
|
+
console.log(` Label: ${apiKey.label}`);
|
|
35
|
+
console.log(` Scopes: ${apiKey.scopes || "(all)"}`);
|
|
36
|
+
console.log(` Expires: ${apiKey.expires_at ?? "never"}`);
|
|
37
|
+
console.log(`\n Raw key (SAVE THIS — shown only once):\n`);
|
|
38
|
+
console.log(` ${rawKey}\n`);
|
|
39
|
+
|
|
40
|
+
db.close();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
authCommand
|
|
44
|
+
.command("list-keys")
|
|
45
|
+
.description("List all API keys")
|
|
46
|
+
.action(() => {
|
|
47
|
+
const policy = loadPolicy();
|
|
48
|
+
const dataDir = resolveDataDir(policy.data_dir);
|
|
49
|
+
const db = openAuthDb(dataDir);
|
|
50
|
+
|
|
51
|
+
const keys = db.listApiKeys();
|
|
52
|
+
if (keys.length === 0) {
|
|
53
|
+
console.log("No API keys found. Create one with `quint auth create-key`.");
|
|
54
|
+
db.close();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(`${keys.length} API key(s):\n`);
|
|
59
|
+
for (const key of keys) {
|
|
60
|
+
const status = key.revoked
|
|
61
|
+
? "REVOKED"
|
|
62
|
+
: key.expires_at && new Date(key.expires_at) < new Date()
|
|
63
|
+
? "EXPIRED"
|
|
64
|
+
: "active";
|
|
65
|
+
const icon = status === "active" ? "●" : "○";
|
|
66
|
+
console.log(` ${icon} ${key.id} ${key.label} [${status}] scopes=${key.scopes || "*"} created=${key.created_at}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
db.close();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
authCommand
|
|
73
|
+
.command("revoke-key")
|
|
74
|
+
.description("Revoke an API key")
|
|
75
|
+
.requiredOption("--id <id>", "API key ID to revoke")
|
|
76
|
+
.action((opts: { id: string }) => {
|
|
77
|
+
const policy = loadPolicy();
|
|
78
|
+
const dataDir = resolveDataDir(policy.data_dir);
|
|
79
|
+
const db = openAuthDb(dataDir);
|
|
80
|
+
|
|
81
|
+
const key = db.getApiKeyById(opts.id);
|
|
82
|
+
if (!key) {
|
|
83
|
+
console.error(`API key not found: ${opts.id}`);
|
|
84
|
+
db.close();
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (key.revoked) {
|
|
89
|
+
console.log(`Key ${opts.id} is already revoked.`);
|
|
90
|
+
db.close();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
db.revokeApiKey(opts.id);
|
|
95
|
+
// Also revoke any sessions issued to this key
|
|
96
|
+
const revoked = db.revokeSessionsBySubject(opts.id);
|
|
97
|
+
console.log(`Revoked key ${opts.id} (${key.label}).`);
|
|
98
|
+
if (revoked > 0) {
|
|
99
|
+
console.log(` Also revoked ${revoked} active session(s).`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
db.close();
|
|
103
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { loadPolicy } from "@quint-security/core";
|
|
3
|
+
import { startHttpProxy } from "@quint-security/proxy";
|
|
4
|
+
|
|
5
|
+
export const httpProxyCommand = new Command("http-proxy")
|
|
6
|
+
.description("Run as HTTP proxy in front of a remote MCP server")
|
|
7
|
+
.requiredOption("--name <name>", "Name identifier for the proxied server")
|
|
8
|
+
.requiredOption("--port <port>", "Local port to listen on", parseInt)
|
|
9
|
+
.requiredOption("--target <url>", "Remote MCP server URL to proxy to")
|
|
10
|
+
.option("--policy <path>", "Path to policy.json (default: ~/.quint/policy.json)")
|
|
11
|
+
.option("--require-auth", "Require API key authentication on all requests")
|
|
12
|
+
.action(async (opts: { name: string; port: number; target: string; policy?: string; requireAuth?: boolean }) => {
|
|
13
|
+
if (isNaN(opts.port) || opts.port < 1 || opts.port > 65535) {
|
|
14
|
+
process.stderr.write("quint: --port must be a valid port number (1-65535)\n");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
new URL(opts.target);
|
|
20
|
+
} catch {
|
|
21
|
+
process.stderr.write(`quint: --target must be a valid URL\n`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const policy = loadPolicy(opts.policy ? opts.policy : undefined);
|
|
26
|
+
|
|
27
|
+
await startHttpProxy({
|
|
28
|
+
serverName: opts.name,
|
|
29
|
+
port: opts.port,
|
|
30
|
+
targetUrl: opts.target,
|
|
31
|
+
policy,
|
|
32
|
+
requireAuth: opts.requireAuth,
|
|
33
|
+
});
|
|
34
|
+
});
|