create-vellaveto 4.0.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/dist/constants.d.ts +6 -0
- package/dist/constants.js +14 -0
- package/dist/generators/binary.d.ts +9 -0
- package/dist/generators/binary.js +86 -0
- package/dist/generators/docker.d.ts +8 -0
- package/dist/generators/docker.js +122 -0
- package/dist/generators/helm.d.ts +8 -0
- package/dist/generators/helm.js +100 -0
- package/dist/generators/snippets.d.ts +9 -0
- package/dist/generators/snippets.js +147 -0
- package/dist/generators/toml.d.ts +9 -0
- package/dist/generators/toml.js +232 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +74 -0
- package/dist/steps/audit.d.ts +2 -0
- package/dist/steps/audit.js +109 -0
- package/dist/steps/compliance.d.ts +2 -0
- package/dist/steps/compliance.js +26 -0
- package/dist/steps/detection.d.ts +2 -0
- package/dist/steps/detection.js +52 -0
- package/dist/steps/policies.d.ts +2 -0
- package/dist/steps/policies.js +29 -0
- package/dist/steps/sdk.d.ts +2 -0
- package/dist/steps/sdk.js +25 -0
- package/dist/steps/security.d.ts +2 -0
- package/dist/steps/security.js +61 -0
- package/dist/steps/welcome.d.ts +2 -0
- package/dist/steps/welcome.js +54 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.js +41 -0
- package/dist/wizard.d.ts +5 -0
- package/dist/wizard.js +195 -0
- package/package.json +42 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOML configuration generator.
|
|
3
|
+
*
|
|
4
|
+
* Output must match vellaveto-server/src/setup_wizard.rs generate_config_toml()
|
|
5
|
+
* (lines ~1596-1844) so configs are interchangeable between the web wizard and
|
|
6
|
+
* this CLI wizard.
|
|
7
|
+
*/
|
|
8
|
+
import { escapeTomlString } from "../utils.js";
|
|
9
|
+
export function generateToml(state) {
|
|
10
|
+
let toml = "";
|
|
11
|
+
// Header
|
|
12
|
+
toml += "# Vellaveto Configuration\n";
|
|
13
|
+
toml += "# Generated by the setup wizard\n\n";
|
|
14
|
+
// API key note
|
|
15
|
+
if (state.apiKey) {
|
|
16
|
+
toml +=
|
|
17
|
+
"# API key configured via wizard — set VELLAVETO_API_KEY environment variable\n\n";
|
|
18
|
+
}
|
|
19
|
+
// CORS origins
|
|
20
|
+
if (state.corsOrigins.length > 0) {
|
|
21
|
+
toml +=
|
|
22
|
+
"# Allowed CORS origins (also settable via VELLAVETO_CORS_ORIGINS env var)\n";
|
|
23
|
+
toml += "allowed_origins = [";
|
|
24
|
+
for (let i = 0; i < state.corsOrigins.length; i++) {
|
|
25
|
+
if (i > 0)
|
|
26
|
+
toml += ", ";
|
|
27
|
+
toml += `"${escapeTomlString(state.corsOrigins[i])}"`;
|
|
28
|
+
}
|
|
29
|
+
toml += "]\n\n";
|
|
30
|
+
}
|
|
31
|
+
// Policies
|
|
32
|
+
toml += "# ─── Policies ───────────────────────────────────────────────\n\n";
|
|
33
|
+
toml += generatePolicyPreset(state.policyPreset);
|
|
34
|
+
// Detection
|
|
35
|
+
toml += "# ─── Detection ──────────────────────────────────────────────\n\n";
|
|
36
|
+
toml += "[injection]\n";
|
|
37
|
+
toml += `enabled = ${state.injectionEnabled}\n`;
|
|
38
|
+
if (state.injectionEnabled) {
|
|
39
|
+
toml += `blocking = ${state.injectionBlocking}\n`;
|
|
40
|
+
}
|
|
41
|
+
toml += "\n";
|
|
42
|
+
toml += "[dlp]\n";
|
|
43
|
+
toml += `enabled = ${state.dlpEnabled}\n`;
|
|
44
|
+
if (state.dlpEnabled) {
|
|
45
|
+
toml += `blocking = ${state.dlpBlocking}\n`;
|
|
46
|
+
}
|
|
47
|
+
toml += "\n";
|
|
48
|
+
if (state.behavioralEnabled) {
|
|
49
|
+
toml += "[behavioral]\n";
|
|
50
|
+
toml += "enabled = true\n\n";
|
|
51
|
+
}
|
|
52
|
+
// Audit
|
|
53
|
+
toml += "# ─── Audit ──────────────────────────────────────────────────\n\n";
|
|
54
|
+
toml += "[audit]\n";
|
|
55
|
+
toml += `redaction_level = "${escapeTomlString(state.redactionLevel)}"\n`;
|
|
56
|
+
if (state.checkpointInterval > 0) {
|
|
57
|
+
toml += `checkpoint_interval = ${state.checkpointInterval}\n`;
|
|
58
|
+
}
|
|
59
|
+
toml += "\n";
|
|
60
|
+
// Audit export
|
|
61
|
+
if (state.auditExportFormat !== "none") {
|
|
62
|
+
toml += "[audit_export]\n";
|
|
63
|
+
toml += `format = "${escapeTomlString(state.auditExportFormat)}"\n`;
|
|
64
|
+
if (state.auditExportTarget) {
|
|
65
|
+
toml += `target = "${escapeTomlString(state.auditExportTarget)}"\n`;
|
|
66
|
+
}
|
|
67
|
+
toml += "\n";
|
|
68
|
+
}
|
|
69
|
+
// Compliance
|
|
70
|
+
const hasCompliance = state.complianceFrameworks.length > 0;
|
|
71
|
+
if (hasCompliance) {
|
|
72
|
+
toml +=
|
|
73
|
+
"# ─── Compliance ─────────────────────────────────────────────\n\n";
|
|
74
|
+
toml += "[compliance]\n";
|
|
75
|
+
if (state.complianceFrameworks.includes("eu_ai_act")) {
|
|
76
|
+
toml += "\n[compliance.eu_ai_act]\n";
|
|
77
|
+
toml += "enabled = true\n";
|
|
78
|
+
toml += "transparency_marking = true\n";
|
|
79
|
+
}
|
|
80
|
+
if (state.complianceFrameworks.includes("soc2")) {
|
|
81
|
+
toml += "\n[compliance.soc2]\n";
|
|
82
|
+
toml += "enabled = true\n";
|
|
83
|
+
}
|
|
84
|
+
if (state.complianceFrameworks.includes("iso42001")) {
|
|
85
|
+
toml += "\n[compliance.iso42001]\n";
|
|
86
|
+
toml += "enabled = true\n";
|
|
87
|
+
}
|
|
88
|
+
if (state.complianceFrameworks.includes("nis2")) {
|
|
89
|
+
toml +=
|
|
90
|
+
"\n# NIS2 compliance enabled — incident reporting and supply chain checks active\n";
|
|
91
|
+
}
|
|
92
|
+
if (state.complianceFrameworks.includes("dora")) {
|
|
93
|
+
toml +=
|
|
94
|
+
"\n# DORA compliance enabled — ICT risk management and incident tracking active\n";
|
|
95
|
+
}
|
|
96
|
+
toml += "\n";
|
|
97
|
+
}
|
|
98
|
+
return toml;
|
|
99
|
+
}
|
|
100
|
+
function generatePolicyPreset(preset) {
|
|
101
|
+
switch (preset) {
|
|
102
|
+
case "strict":
|
|
103
|
+
return generateStrictPreset();
|
|
104
|
+
case "balanced":
|
|
105
|
+
return generateBalancedPreset();
|
|
106
|
+
case "permissive":
|
|
107
|
+
return generatePermissivePreset();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function generateStrictPreset() {
|
|
111
|
+
let toml = "";
|
|
112
|
+
toml +=
|
|
113
|
+
"# Policy preset: Strict (deny-by-default, maximum security)\n\n";
|
|
114
|
+
// Default deny
|
|
115
|
+
toml += "[[policies]]\n";
|
|
116
|
+
toml += 'id = "default-deny"\n';
|
|
117
|
+
toml += 'name = "Default deny all"\n';
|
|
118
|
+
toml += 'policy_type = "Deny"\n';
|
|
119
|
+
toml += "priority = 0\n";
|
|
120
|
+
toml += 'tool = "*"\n';
|
|
121
|
+
toml += 'function = "*"\n\n';
|
|
122
|
+
// Block credentials
|
|
123
|
+
toml += "[[policies]]\n";
|
|
124
|
+
toml += 'id = "block-credentials"\n';
|
|
125
|
+
toml += 'name = "Block credential access"\n';
|
|
126
|
+
toml += 'policy_type = "Deny"\n';
|
|
127
|
+
toml += "priority = 100\n";
|
|
128
|
+
toml += 'tool = "*"\n';
|
|
129
|
+
toml += 'function = "*"\n\n';
|
|
130
|
+
toml += "[policies.path_rules]\n";
|
|
131
|
+
toml +=
|
|
132
|
+
'blocked_patterns = ["**/.env", "**/*.key", "**/*.pem", "**/credentials*", "**/.ssh/**", "**/.aws/**"]\n\n';
|
|
133
|
+
// Block exfiltration
|
|
134
|
+
toml += "[[policies]]\n";
|
|
135
|
+
toml += 'id = "block-exfiltration"\n';
|
|
136
|
+
toml += 'name = "Block data exfiltration"\n';
|
|
137
|
+
toml += 'policy_type = "Deny"\n';
|
|
138
|
+
toml += "priority = 100\n";
|
|
139
|
+
toml += 'tool = "*"\n';
|
|
140
|
+
toml += 'function = "*"\n\n';
|
|
141
|
+
toml += "[policies.network_rules]\n";
|
|
142
|
+
toml +=
|
|
143
|
+
'blocked_domains = ["*.pastebin.com", "*.transfer.sh", "*.ngrok.io"]\n\n';
|
|
144
|
+
// Require approval for destructive
|
|
145
|
+
toml += "[[policies]]\n";
|
|
146
|
+
toml += 'id = "approve-destructive"\n';
|
|
147
|
+
toml += 'name = "Require approval for destructive operations"\n';
|
|
148
|
+
toml += 'policy_type = "RequireApproval"\n';
|
|
149
|
+
toml += "priority = 50\n";
|
|
150
|
+
toml += 'tool = "*"\n';
|
|
151
|
+
toml += 'function = "*"\n\n';
|
|
152
|
+
toml += "[policies.path_rules]\n";
|
|
153
|
+
toml += 'write_patterns = ["**/*"]\n\n';
|
|
154
|
+
return toml;
|
|
155
|
+
}
|
|
156
|
+
function generateBalancedPreset() {
|
|
157
|
+
let toml = "";
|
|
158
|
+
toml +=
|
|
159
|
+
"# Policy preset: Balanced (deny-by-default, read allowed, writes require approval)\n\n";
|
|
160
|
+
// Default deny
|
|
161
|
+
toml += "[[policies]]\n";
|
|
162
|
+
toml += 'id = "default-deny"\n';
|
|
163
|
+
toml += 'name = "Default deny all"\n';
|
|
164
|
+
toml += 'policy_type = "Deny"\n';
|
|
165
|
+
toml += "priority = 0\n";
|
|
166
|
+
toml += 'tool = "*"\n';
|
|
167
|
+
toml += 'function = "*"\n\n';
|
|
168
|
+
// Block credentials
|
|
169
|
+
toml += "[[policies]]\n";
|
|
170
|
+
toml += 'id = "block-credentials"\n';
|
|
171
|
+
toml += 'name = "Block credential access"\n';
|
|
172
|
+
toml += 'policy_type = "Deny"\n';
|
|
173
|
+
toml += "priority = 100\n";
|
|
174
|
+
toml += 'tool = "*"\n';
|
|
175
|
+
toml += 'function = "*"\n\n';
|
|
176
|
+
toml += "[policies.path_rules]\n";
|
|
177
|
+
toml +=
|
|
178
|
+
'blocked_patterns = ["**/.env", "**/*.key", "**/*.pem", "**/credentials*", "**/.ssh/**", "**/.aws/**"]\n\n';
|
|
179
|
+
// Allow reads
|
|
180
|
+
toml += "[[policies]]\n";
|
|
181
|
+
toml += 'id = "allow-reads"\n';
|
|
182
|
+
toml += 'name = "Allow file reads"\n';
|
|
183
|
+
toml += 'policy_type = "Allow"\n';
|
|
184
|
+
toml += "priority = 50\n";
|
|
185
|
+
toml += 'tool = "*"\n';
|
|
186
|
+
toml += 'function = "read*"\n\n';
|
|
187
|
+
// Require approval for writes
|
|
188
|
+
toml += "[[policies]]\n";
|
|
189
|
+
toml += 'id = "approve-writes"\n';
|
|
190
|
+
toml += 'name = "Require approval for file writes"\n';
|
|
191
|
+
toml += 'policy_type = "RequireApproval"\n';
|
|
192
|
+
toml += "priority = 50\n";
|
|
193
|
+
toml += 'tool = "*"\n';
|
|
194
|
+
toml += 'function = "write*"\n\n';
|
|
195
|
+
return toml;
|
|
196
|
+
}
|
|
197
|
+
function generatePermissivePreset() {
|
|
198
|
+
let toml = "";
|
|
199
|
+
toml +=
|
|
200
|
+
"# Policy preset: Permissive (allow-by-default, block credentials and exfiltration)\n\n";
|
|
201
|
+
// Default allow
|
|
202
|
+
toml += "[[policies]]\n";
|
|
203
|
+
toml += 'id = "default-allow"\n';
|
|
204
|
+
toml += 'name = "Default allow all"\n';
|
|
205
|
+
toml += 'policy_type = "Allow"\n';
|
|
206
|
+
toml += "priority = 0\n";
|
|
207
|
+
toml += 'tool = "*"\n';
|
|
208
|
+
toml += 'function = "*"\n\n';
|
|
209
|
+
// Block credentials
|
|
210
|
+
toml += "[[policies]]\n";
|
|
211
|
+
toml += 'id = "block-credentials"\n';
|
|
212
|
+
toml += 'name = "Block credential access"\n';
|
|
213
|
+
toml += 'policy_type = "Deny"\n';
|
|
214
|
+
toml += "priority = 100\n";
|
|
215
|
+
toml += 'tool = "*"\n';
|
|
216
|
+
toml += 'function = "*"\n\n';
|
|
217
|
+
toml += "[policies.path_rules]\n";
|
|
218
|
+
toml +=
|
|
219
|
+
'blocked_patterns = ["**/.env", "**/*.key", "**/*.pem", "**/credentials*", "**/.ssh/**", "**/.aws/**"]\n\n';
|
|
220
|
+
// Block exfiltration
|
|
221
|
+
toml += "[[policies]]\n";
|
|
222
|
+
toml += 'id = "block-exfiltration"\n';
|
|
223
|
+
toml += 'name = "Block data exfiltration"\n';
|
|
224
|
+
toml += 'policy_type = "Deny"\n';
|
|
225
|
+
toml += "priority = 100\n";
|
|
226
|
+
toml += 'tool = "*"\n';
|
|
227
|
+
toml += 'function = "*"\n\n';
|
|
228
|
+
toml += "[policies.network_rules]\n";
|
|
229
|
+
toml +=
|
|
230
|
+
'blocked_domains = ["*.pastebin.com", "*.transfer.sh", "*.ngrok.io"]\n\n';
|
|
231
|
+
return toml;
|
|
232
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-vellaveto — CLI setup wizard for Vellaveto MCP Tool Firewall
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx create-vellaveto Interactive wizard (creates ./vellaveto/)
|
|
7
|
+
* npx create-vellaveto my-project Write files to ./my-project/
|
|
8
|
+
* npx create-vellaveto --help Show help
|
|
9
|
+
* npx create-vellaveto --version Show version
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-vellaveto — CLI setup wizard for Vellaveto MCP Tool Firewall
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx create-vellaveto Interactive wizard (creates ./vellaveto/)
|
|
7
|
+
* npx create-vellaveto my-project Write files to ./my-project/
|
|
8
|
+
* npx create-vellaveto --help Show help
|
|
9
|
+
* npx create-vellaveto --version Show version
|
|
10
|
+
*/
|
|
11
|
+
import { VERSION } from "./constants.js";
|
|
12
|
+
import { runWizard } from "./wizard.js";
|
|
13
|
+
function printHelp() {
|
|
14
|
+
console.log(`
|
|
15
|
+
create-vellaveto v${VERSION}
|
|
16
|
+
|
|
17
|
+
Setup wizard for Vellaveto — MCP Tool Firewall
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
npx create-vellaveto [project-directory]
|
|
21
|
+
|
|
22
|
+
Arguments:
|
|
23
|
+
project-directory Directory to create (default: vellaveto)
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--help, -h Show this help message
|
|
27
|
+
--version, -v Show version number
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
npx create-vellaveto # Creates ./vellaveto/ with all files
|
|
31
|
+
npx create-vellaveto my-project # Creates ./my-project/ with all files
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
function main() {
|
|
35
|
+
const args = process.argv.slice(2);
|
|
36
|
+
let projectDir;
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
const arg = args[i];
|
|
39
|
+
switch (arg) {
|
|
40
|
+
case "--help":
|
|
41
|
+
case "-h":
|
|
42
|
+
printHelp();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
break;
|
|
45
|
+
case "--version":
|
|
46
|
+
case "-v":
|
|
47
|
+
console.log(VERSION);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
break;
|
|
50
|
+
// Keep --output as hidden alias for backwards compat
|
|
51
|
+
case "--output":
|
|
52
|
+
case "-o":
|
|
53
|
+
i++;
|
|
54
|
+
if (i >= args.length) {
|
|
55
|
+
console.error("Error: --output requires a directory path");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
projectDir = args[i];
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
if (arg.startsWith("-")) {
|
|
62
|
+
console.error(`Unknown option: ${arg}`);
|
|
63
|
+
console.error("Run with --help for usage information.");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
projectDir = arg;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
runWizard(projectDir).catch((err) => {
|
|
70
|
+
console.error("Fatal error:", err);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
main();
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
export async function auditStep(state) {
|
|
3
|
+
const redaction = await p.select({
|
|
4
|
+
message: "Audit log redaction level",
|
|
5
|
+
options: [
|
|
6
|
+
{
|
|
7
|
+
value: "off",
|
|
8
|
+
label: "Off",
|
|
9
|
+
hint: "No redaction — full parameters logged (dev only)",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: "low",
|
|
13
|
+
label: "Low (recommended)",
|
|
14
|
+
hint: "Redact known secrets (API keys, tokens)",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
value: "high",
|
|
18
|
+
label: "High",
|
|
19
|
+
hint: "Redact all parameter values",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
initialValue: "low",
|
|
23
|
+
});
|
|
24
|
+
if (p.isCancel(redaction)) {
|
|
25
|
+
p.cancel("Setup cancelled.");
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
state.redactionLevel = redaction;
|
|
29
|
+
const format = await p.select({
|
|
30
|
+
message: "Audit export format",
|
|
31
|
+
options: [
|
|
32
|
+
{
|
|
33
|
+
value: "none",
|
|
34
|
+
label: "None",
|
|
35
|
+
hint: "Local file logging only",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
value: "jsonl",
|
|
39
|
+
label: "JSONL",
|
|
40
|
+
hint: "JSON Lines — one event per line",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
value: "cef",
|
|
44
|
+
label: "CEF",
|
|
45
|
+
hint: "Common Event Format — for SIEM integration",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: "webhook",
|
|
49
|
+
label: "Webhook",
|
|
50
|
+
hint: "POST events to an HTTP endpoint",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
if (p.isCancel(format)) {
|
|
55
|
+
p.cancel("Setup cancelled.");
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
state.auditExportFormat = format;
|
|
59
|
+
if (format === "webhook") {
|
|
60
|
+
const target = await p.text({
|
|
61
|
+
message: "Webhook URL",
|
|
62
|
+
placeholder: "https://example.com/vellaveto-events",
|
|
63
|
+
validate(value) {
|
|
64
|
+
try {
|
|
65
|
+
const url = new URL(value);
|
|
66
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
67
|
+
return "URL must use http:// or https://";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return "Please enter a valid URL";
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
if (p.isCancel(target)) {
|
|
76
|
+
p.cancel("Setup cancelled.");
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
state.auditExportTarget = target;
|
|
80
|
+
}
|
|
81
|
+
else if (format === "jsonl" || format === "cef") {
|
|
82
|
+
const target = await p.text({
|
|
83
|
+
message: "Export file path",
|
|
84
|
+
placeholder: `/var/log/vellaveto/export.${format}`,
|
|
85
|
+
defaultValue: `/var/log/vellaveto/export.${format}`,
|
|
86
|
+
});
|
|
87
|
+
if (p.isCancel(target)) {
|
|
88
|
+
p.cancel("Setup cancelled.");
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
state.auditExportTarget = target;
|
|
92
|
+
}
|
|
93
|
+
const checkpoint = await p.text({
|
|
94
|
+
message: "Audit checkpoint interval (seconds, 0 to disable)",
|
|
95
|
+
placeholder: "300",
|
|
96
|
+
defaultValue: "300",
|
|
97
|
+
validate(value) {
|
|
98
|
+
const n = Number(value);
|
|
99
|
+
if (isNaN(n) || n < 0 || !Number.isInteger(n)) {
|
|
100
|
+
return "Must be a non-negative integer";
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
if (p.isCancel(checkpoint)) {
|
|
105
|
+
p.cancel("Setup cancelled.");
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
state.checkpointInterval = Number(checkpoint);
|
|
109
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
const FRAMEWORKS = [
|
|
3
|
+
{ value: "eu_ai_act", label: "EU AI Act", hint: "Transparency marking, risk assessment" },
|
|
4
|
+
{ value: "nis2", label: "NIS2", hint: "Incident reporting, supply chain checks" },
|
|
5
|
+
{ value: "dora", label: "DORA", hint: "ICT risk management, incident tracking" },
|
|
6
|
+
{ value: "soc2", label: "SOC 2", hint: "Trust service criteria auditing" },
|
|
7
|
+
{ value: "iso42001", label: "ISO 42001", hint: "AI management system standard" },
|
|
8
|
+
];
|
|
9
|
+
export async function complianceStep(state) {
|
|
10
|
+
p.log.info("Select which compliance frameworks to enable:");
|
|
11
|
+
const selected = [];
|
|
12
|
+
for (const fw of FRAMEWORKS) {
|
|
13
|
+
const enabled = await p.confirm({
|
|
14
|
+
message: `${fw.label} — ${fw.hint}`,
|
|
15
|
+
initialValue: false,
|
|
16
|
+
});
|
|
17
|
+
if (p.isCancel(enabled)) {
|
|
18
|
+
p.cancel("Setup cancelled.");
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
if (enabled) {
|
|
22
|
+
selected.push(fw.value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
state.complianceFrameworks = selected;
|
|
26
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
export async function detectionStep(state) {
|
|
3
|
+
const injectionEnabled = await p.confirm({
|
|
4
|
+
message: "Enable injection detection?",
|
|
5
|
+
initialValue: true,
|
|
6
|
+
});
|
|
7
|
+
if (p.isCancel(injectionEnabled)) {
|
|
8
|
+
p.cancel("Setup cancelled.");
|
|
9
|
+
process.exit(0);
|
|
10
|
+
}
|
|
11
|
+
state.injectionEnabled = injectionEnabled;
|
|
12
|
+
if (injectionEnabled) {
|
|
13
|
+
const injectionBlocking = await p.confirm({
|
|
14
|
+
message: "Block requests with detected injections? (vs. log-only)",
|
|
15
|
+
initialValue: false,
|
|
16
|
+
});
|
|
17
|
+
if (p.isCancel(injectionBlocking)) {
|
|
18
|
+
p.cancel("Setup cancelled.");
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
state.injectionBlocking = injectionBlocking;
|
|
22
|
+
}
|
|
23
|
+
const dlpEnabled = await p.confirm({
|
|
24
|
+
message: "Enable DLP (Data Loss Prevention) scanning?",
|
|
25
|
+
initialValue: true,
|
|
26
|
+
});
|
|
27
|
+
if (p.isCancel(dlpEnabled)) {
|
|
28
|
+
p.cancel("Setup cancelled.");
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
state.dlpEnabled = dlpEnabled;
|
|
32
|
+
if (dlpEnabled) {
|
|
33
|
+
const dlpBlocking = await p.confirm({
|
|
34
|
+
message: "Block requests with DLP findings? (vs. log-only)",
|
|
35
|
+
initialValue: false,
|
|
36
|
+
});
|
|
37
|
+
if (p.isCancel(dlpBlocking)) {
|
|
38
|
+
p.cancel("Setup cancelled.");
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
state.dlpBlocking = dlpBlocking;
|
|
42
|
+
}
|
|
43
|
+
const behavioralEnabled = await p.confirm({
|
|
44
|
+
message: "Enable behavioral anomaly detection?",
|
|
45
|
+
initialValue: false,
|
|
46
|
+
});
|
|
47
|
+
if (p.isCancel(behavioralEnabled)) {
|
|
48
|
+
p.cancel("Setup cancelled.");
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
state.behavioralEnabled = behavioralEnabled;
|
|
52
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
export async function policiesStep(state) {
|
|
3
|
+
const preset = await p.select({
|
|
4
|
+
message: "Select a policy preset",
|
|
5
|
+
options: [
|
|
6
|
+
{
|
|
7
|
+
value: "strict",
|
|
8
|
+
label: "Strict",
|
|
9
|
+
hint: "Deny-by-default. Block credentials + exfiltration. Destructive ops require approval.",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
value: "balanced",
|
|
13
|
+
label: "Balanced (recommended)",
|
|
14
|
+
hint: "Deny-by-default. Block credentials. Reads allowed, writes require approval.",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
value: "permissive",
|
|
18
|
+
label: "Permissive",
|
|
19
|
+
hint: "Allow-by-default. Only block credentials and exfiltration attempts.",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
initialValue: "balanced",
|
|
23
|
+
});
|
|
24
|
+
if (p.isCancel(preset)) {
|
|
25
|
+
p.cancel("Setup cancelled.");
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
state.policyPreset = preset;
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { generateSnippet, installCommand } from "../generators/snippets.js";
|
|
4
|
+
export async function sdkStep(state) {
|
|
5
|
+
const language = await p.select({
|
|
6
|
+
message: "Show integration snippet for which SDK?",
|
|
7
|
+
options: [
|
|
8
|
+
{ value: "python", label: "Python", hint: "pip install vellaveto" },
|
|
9
|
+
{ value: "typescript", label: "TypeScript", hint: "npm install vellaveto" },
|
|
10
|
+
{ value: "go", label: "Go", hint: "go get vellaveto" },
|
|
11
|
+
{ value: "java", label: "Java", hint: "Maven / Gradle" },
|
|
12
|
+
{ value: "skip", label: "Skip", hint: "No snippet needed" },
|
|
13
|
+
],
|
|
14
|
+
});
|
|
15
|
+
if (p.isCancel(language)) {
|
|
16
|
+
p.cancel("Setup cancelled.");
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
state.sdkLanguage = language;
|
|
20
|
+
if (language !== "skip") {
|
|
21
|
+
const install = installCommand(language);
|
|
22
|
+
const snippet = generateSnippet(language, state.apiKey);
|
|
23
|
+
p.note(`${pc.dim("Install:")}\n${install}\n\n${pc.dim("Usage:")}\n${snippet}`, `${language.charAt(0).toUpperCase() + language.slice(1)} SDK`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { generateApiKey, isValidOrigin } from "../utils.js";
|
|
4
|
+
export async function securityStep(state) {
|
|
5
|
+
const generatedKey = generateApiKey();
|
|
6
|
+
p.log.info(`Generated API key: ${pc.cyan(generatedKey)}`);
|
|
7
|
+
const useGenerated = await p.confirm({
|
|
8
|
+
message: "Use this API key?",
|
|
9
|
+
initialValue: true,
|
|
10
|
+
});
|
|
11
|
+
if (p.isCancel(useGenerated)) {
|
|
12
|
+
p.cancel("Setup cancelled.");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
if (useGenerated) {
|
|
16
|
+
state.apiKey = generatedKey;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const customKey = await p.text({
|
|
20
|
+
message: "Enter your API key",
|
|
21
|
+
validate(value) {
|
|
22
|
+
if (value.length < 8)
|
|
23
|
+
return "API key must be at least 8 characters";
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
if (p.isCancel(customKey)) {
|
|
27
|
+
p.cancel("Setup cancelled.");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
state.apiKey = customKey;
|
|
31
|
+
}
|
|
32
|
+
const corsInput = await p.text({
|
|
33
|
+
message: "Allowed CORS origins (comma-separated, or * for all)",
|
|
34
|
+
placeholder: "http://localhost:3000",
|
|
35
|
+
defaultValue: "",
|
|
36
|
+
});
|
|
37
|
+
if (p.isCancel(corsInput)) {
|
|
38
|
+
p.cancel("Setup cancelled.");
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
if (corsInput.trim()) {
|
|
42
|
+
const origins = corsInput
|
|
43
|
+
.split(",")
|
|
44
|
+
.map((s) => s.trim())
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
const invalid = origins.filter((o) => !isValidOrigin(o));
|
|
47
|
+
if (invalid.length > 0) {
|
|
48
|
+
p.log.warn(`Skipping invalid origins: ${invalid.join(", ")}`);
|
|
49
|
+
}
|
|
50
|
+
state.corsOrigins = origins.filter((o) => isValidOrigin(o));
|
|
51
|
+
}
|
|
52
|
+
const anonymous = await p.confirm({
|
|
53
|
+
message: "Allow anonymous (unauthenticated) evaluate requests?",
|
|
54
|
+
initialValue: false,
|
|
55
|
+
});
|
|
56
|
+
if (p.isCancel(anonymous)) {
|
|
57
|
+
p.cancel("Setup cancelled.");
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
state.anonymousMode = anonymous;
|
|
61
|
+
}
|