atabey 0.1.1-beta → 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/README.md +144 -78
- package/dist/atabey/src/cli/commands/coverage.js +17 -12
- package/dist/atabey/src/cli/commands/coverage.js.map +1 -1
- package/dist/atabey/src/cli/commands/lint.d.ts +2 -1
- package/dist/atabey/src/cli/commands/lint.js +20 -1
- package/dist/atabey/src/cli/commands/lint.js.map +1 -1
- package/dist/atabey/src/cli/commands/mcp.js +56 -23
- package/dist/atabey/src/cli/commands/mcp.js.map +1 -1
- package/dist/atabey/src/cli/commands/orchestrate.d.ts +6 -6
- package/dist/shared/src/agent-identity.d.ts +1 -34
- package/dist/shared/src/agent-identity.js +1 -76
- package/dist/shared/src/agent-identity.js.map +1 -1
- package/dist/shared/src/api-catalog.d.ts +1 -57
- package/dist/shared/src/api-catalog.js +1 -253
- package/dist/shared/src/api-catalog.js.map +1 -1
- package/dist/shared/src/audit-chain.d.ts +1 -80
- package/dist/shared/src/audit-chain.js +1 -261
- package/dist/shared/src/audit-chain.js.map +1 -1
- package/dist/shared/src/audit.d.ts +1 -121
- package/dist/shared/src/audit.js +1 -321
- package/dist/shared/src/audit.js.map +1 -1
- package/dist/shared/src/constants.d.ts +1 -150
- package/dist/shared/src/constants.js +1 -211
- package/dist/shared/src/constants.js.map +1 -1
- package/dist/shared/src/database.d.ts +1 -6
- package/dist/shared/src/database.js +1 -15
- package/dist/shared/src/database.js.map +1 -1
- package/dist/shared/src/errors.d.ts +1 -46
- package/dist/shared/src/errors.js +1 -68
- package/dist/shared/src/errors.js.map +1 -1
- package/dist/shared/src/file-lock.d.ts +1 -93
- package/dist/shared/src/file-lock.js +1 -239
- package/dist/shared/src/file-lock.js.map +1 -1
- package/dist/shared/src/fs.d.ts +1 -9
- package/dist/shared/src/fs.js +1 -53
- package/dist/shared/src/fs.js.map +1 -1
- package/dist/shared/src/index.d.ts +2 -31
- package/dist/shared/src/index.js +2 -31
- package/dist/shared/src/index.js.map +1 -1
- package/dist/shared/src/job-queue.d.ts +1 -76
- package/dist/shared/src/job-queue.js +1 -199
- package/dist/shared/src/job-queue.js.map +1 -1
- package/dist/shared/src/lock.d.ts +1 -82
- package/dist/shared/src/lock.js +1 -158
- package/dist/shared/src/lock.js.map +1 -1
- package/dist/shared/src/logger.d.ts +1 -31
- package/dist/shared/src/logger.js +1 -152
- package/dist/shared/src/logger.js.map +1 -1
- package/dist/shared/src/memory-prune.d.ts +1 -18
- package/dist/shared/src/memory-prune.js +1 -47
- package/dist/shared/src/memory-prune.js.map +1 -1
- package/dist/shared/src/migrations.d.ts +1 -69
- package/dist/shared/src/migrations.js +1 -393
- package/dist/shared/src/migrations.js.map +1 -1
- package/dist/shared/src/modules/agent/agent-identity.d.ts +34 -0
- package/dist/shared/src/modules/agent/agent-identity.js +77 -0
- package/dist/shared/src/modules/agent/agent-identity.js.map +1 -0
- package/dist/shared/src/modules/agent/api-catalog.d.ts +57 -0
- package/dist/shared/src/modules/agent/api-catalog.js +254 -0
- package/dist/shared/src/modules/agent/api-catalog.js.map +1 -0
- package/dist/shared/src/modules/agent/index.d.ts +3 -0
- package/dist/shared/src/modules/agent/index.js +4 -0
- package/dist/shared/src/modules/agent/index.js.map +1 -0
- package/dist/shared/src/modules/agent/tokenizer.d.ts +54 -0
- package/dist/shared/src/modules/agent/tokenizer.js +272 -0
- package/dist/shared/src/modules/agent/tokenizer.js.map +1 -0
- package/dist/shared/src/modules/concurrency/file-lock.d.ts +93 -0
- package/dist/shared/src/modules/concurrency/file-lock.js +240 -0
- package/dist/shared/src/modules/concurrency/file-lock.js.map +1 -0
- package/dist/shared/src/modules/concurrency/index.d.ts +6 -0
- package/dist/shared/src/modules/concurrency/index.js +7 -0
- package/dist/shared/src/modules/concurrency/index.js.map +1 -0
- package/dist/shared/src/modules/concurrency/job-queue.d.ts +76 -0
- package/dist/shared/src/modules/concurrency/job-queue.js +200 -0
- package/dist/shared/src/modules/concurrency/job-queue.js.map +1 -0
- package/dist/shared/src/modules/concurrency/lock.d.ts +82 -0
- package/dist/shared/src/modules/concurrency/lock.js +159 -0
- package/dist/shared/src/modules/concurrency/lock.js.map +1 -0
- package/dist/shared/src/modules/concurrency/resilience.d.ts +84 -0
- package/dist/shared/src/modules/concurrency/resilience.js +217 -0
- package/dist/shared/src/modules/concurrency/resilience.js.map +1 -0
- package/dist/shared/src/modules/concurrency/worker-pool.d.ts +52 -0
- package/dist/shared/src/modules/concurrency/worker-pool.js +229 -0
- package/dist/shared/src/modules/concurrency/worker-pool.js.map +1 -0
- package/dist/shared/src/modules/concurrency/worker-thread.d.ts +21 -0
- package/dist/shared/src/modules/concurrency/worker-thread.js +67 -0
- package/dist/shared/src/modules/concurrency/worker-thread.js.map +1 -0
- package/dist/shared/src/modules/index.d.ts +14 -0
- package/dist/shared/src/modules/index.js +15 -0
- package/dist/shared/src/modules/index.js.map +1 -0
- package/dist/shared/src/modules/observability/audit-chain.d.ts +80 -0
- package/dist/shared/src/modules/observability/audit-chain.js +262 -0
- package/dist/shared/src/modules/observability/audit-chain.js.map +1 -0
- package/dist/shared/src/modules/observability/audit.d.ts +121 -0
- package/dist/shared/src/modules/observability/audit.js +322 -0
- package/dist/shared/src/modules/observability/audit.js.map +1 -0
- package/dist/shared/src/modules/observability/index.d.ts +4 -0
- package/dist/shared/src/modules/observability/index.js +5 -0
- package/dist/shared/src/modules/observability/index.js.map +1 -0
- package/dist/shared/src/modules/observability/logger.d.ts +31 -0
- package/dist/shared/src/modules/observability/logger.js +153 -0
- package/dist/shared/src/modules/observability/logger.js.map +1 -0
- package/dist/shared/src/modules/observability/otel.d.ts +39 -0
- package/dist/shared/src/modules/observability/otel.js +93 -0
- package/dist/shared/src/modules/observability/otel.js.map +1 -0
- package/dist/shared/src/modules/primitives/constants.d.ts +150 -0
- package/dist/shared/src/modules/primitives/constants.js +212 -0
- package/dist/shared/src/modules/primitives/constants.js.map +1 -0
- package/dist/shared/src/modules/primitives/errors.d.ts +46 -0
- package/dist/shared/src/modules/primitives/errors.js +69 -0
- package/dist/shared/src/modules/primitives/errors.js.map +1 -0
- package/dist/shared/src/modules/primitives/fs.d.ts +9 -0
- package/dist/shared/src/modules/primitives/fs.js +54 -0
- package/dist/shared/src/modules/primitives/fs.js.map +1 -0
- package/dist/shared/src/modules/primitives/index.d.ts +7 -0
- package/dist/shared/src/modules/primitives/index.js +8 -0
- package/dist/shared/src/modules/primitives/index.js.map +1 -0
- package/dist/shared/src/modules/primitives/outbound-http.d.ts +32 -0
- package/dist/shared/src/modules/primitives/outbound-http.js +88 -0
- package/dist/shared/src/modules/primitives/outbound-http.js.map +1 -0
- package/dist/shared/src/modules/primitives/string.d.ts +1 -0
- package/dist/shared/src/modules/primitives/string.js +30 -0
- package/dist/shared/src/modules/primitives/string.js.map +1 -0
- package/dist/shared/src/modules/primitives/time.d.ts +5 -0
- package/dist/shared/src/modules/primitives/time.js +35 -0
- package/dist/shared/src/modules/primitives/time.js.map +1 -0
- package/dist/shared/src/modules/primitives/types.d.ts +27 -0
- package/dist/shared/src/modules/primitives/types.js +17 -0
- package/dist/shared/src/modules/primitives/types.js.map +1 -0
- package/dist/shared/src/modules/sandbox/index.d.ts +3 -0
- package/dist/shared/src/modules/sandbox/index.js +4 -0
- package/dist/shared/src/modules/sandbox/index.js.map +1 -0
- package/dist/shared/src/modules/sandbox/sandbox-fs.d.ts +28 -0
- package/dist/shared/src/modules/sandbox/sandbox-fs.js +143 -0
- package/dist/shared/src/modules/sandbox/sandbox-fs.js.map +1 -0
- package/dist/shared/src/modules/sandbox/sandbox-runtime.d.ts +78 -0
- package/dist/shared/src/modules/sandbox/sandbox-runtime.js +282 -0
- package/dist/shared/src/modules/sandbox/sandbox-runtime.js.map +1 -0
- package/dist/shared/src/modules/sandbox/sandbox.d.ts +54 -0
- package/dist/shared/src/modules/sandbox/sandbox.js +179 -0
- package/dist/shared/src/modules/sandbox/sandbox.js.map +1 -0
- package/dist/shared/src/modules/security/index.d.ts +5 -0
- package/dist/shared/src/modules/security/index.js +6 -0
- package/dist/shared/src/modules/security/index.js.map +1 -0
- package/dist/shared/src/modules/security/pii.d.ts +70 -0
- package/dist/shared/src/modules/security/pii.js +276 -0
- package/dist/shared/src/modules/security/pii.js.map +1 -0
- package/dist/shared/src/modules/security/policy-gate.d.ts +42 -0
- package/dist/shared/src/modules/security/policy-gate.js +172 -0
- package/dist/shared/src/modules/security/policy-gate.js.map +1 -0
- package/dist/shared/src/modules/security/retention.d.ts +68 -0
- package/dist/shared/src/modules/security/retention.js +218 -0
- package/dist/shared/src/modules/security/retention.js.map +1 -0
- package/dist/shared/src/modules/security/secrets.d.ts +99 -0
- package/dist/shared/src/modules/security/secrets.js +371 -0
- package/dist/shared/src/modules/security/secrets.js.map +1 -0
- package/dist/shared/src/modules/security/tls.d.ts +87 -0
- package/dist/shared/src/modules/security/tls.js +154 -0
- package/dist/shared/src/modules/security/tls.js.map +1 -0
- package/dist/shared/src/modules/storage/database.d.ts +6 -0
- package/dist/shared/src/modules/storage/database.js +16 -0
- package/dist/shared/src/modules/storage/database.js.map +1 -0
- package/dist/shared/src/modules/storage/index.d.ts +4 -0
- package/dist/shared/src/modules/storage/index.js +5 -0
- package/dist/shared/src/modules/storage/index.js.map +1 -0
- package/dist/shared/src/modules/storage/memory-prune.d.ts +18 -0
- package/dist/shared/src/modules/storage/memory-prune.js +48 -0
- package/dist/shared/src/modules/storage/memory-prune.js.map +1 -0
- package/dist/shared/src/modules/storage/migrations.d.ts +69 -0
- package/dist/shared/src/modules/storage/migrations.js +394 -0
- package/dist/shared/src/modules/storage/migrations.js.map +1 -0
- package/dist/shared/src/modules/storage/storage.d.ts +341 -0
- package/dist/shared/src/modules/storage/storage.js +1004 -0
- package/dist/shared/src/modules/storage/storage.js.map +1 -0
- package/dist/shared/src/otel.d.ts +1 -39
- package/dist/shared/src/otel.js +1 -92
- package/dist/shared/src/otel.js.map +1 -1
- package/dist/shared/src/outbound-http.d.ts +1 -32
- package/dist/shared/src/outbound-http.js +1 -87
- package/dist/shared/src/outbound-http.js.map +1 -1
- package/dist/shared/src/pii.d.ts +1 -70
- package/dist/shared/src/pii.js +1 -275
- package/dist/shared/src/pii.js.map +1 -1
- package/dist/shared/src/policy-gate.d.ts +1 -42
- package/dist/shared/src/policy-gate.js +1 -171
- package/dist/shared/src/policy-gate.js.map +1 -1
- package/dist/shared/src/resilience.d.ts +1 -84
- package/dist/shared/src/resilience.js +1 -216
- package/dist/shared/src/resilience.js.map +1 -1
- package/dist/shared/src/retention.d.ts +1 -68
- package/dist/shared/src/retention.js +1 -217
- package/dist/shared/src/retention.js.map +1 -1
- package/dist/shared/src/sandbox-fs.d.ts +1 -28
- package/dist/shared/src/sandbox-fs.js +1 -142
- package/dist/shared/src/sandbox-fs.js.map +1 -1
- package/dist/shared/src/sandbox-runtime.d.ts +1 -78
- package/dist/shared/src/sandbox-runtime.js +1 -281
- package/dist/shared/src/sandbox-runtime.js.map +1 -1
- package/dist/shared/src/sandbox.d.ts +1 -54
- package/dist/shared/src/sandbox.js +1 -178
- package/dist/shared/src/sandbox.js.map +1 -1
- package/dist/shared/src/secrets.d.ts +1 -99
- package/dist/shared/src/secrets.js +1 -370
- package/dist/shared/src/secrets.js.map +1 -1
- package/dist/shared/src/storage.d.ts +1 -341
- package/dist/shared/src/storage.js +1 -1003
- package/dist/shared/src/storage.js.map +1 -1
- package/dist/shared/src/string.d.ts +1 -1
- package/dist/shared/src/string.js +1 -29
- package/dist/shared/src/string.js.map +1 -1
- package/dist/shared/src/time.d.ts +1 -5
- package/dist/shared/src/time.js +1 -34
- package/dist/shared/src/time.js.map +1 -1
- package/dist/shared/src/tls.d.ts +1 -87
- package/dist/shared/src/tls.js +1 -153
- package/dist/shared/src/tls.js.map +1 -1
- package/dist/shared/src/tokenizer.d.ts +1 -54
- package/dist/shared/src/tokenizer.js +1 -271
- package/dist/shared/src/tokenizer.js.map +1 -1
- package/dist/shared/src/types.d.ts +1 -27
- package/dist/shared/src/types.js +1 -16
- package/dist/shared/src/types.js.map +1 -1
- package/dist/shared/src/worker-pool.d.ts +1 -52
- package/dist/shared/src/worker-pool.js +1 -228
- package/dist/shared/src/worker-pool.js.map +1 -1
- package/dist/shared/src/worker-thread.d.ts +1 -21
- package/dist/shared/src/worker-thread.js +1 -66
- package/dist/shared/src/worker-thread.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret Management & Leak Guard (Phase 1.5)
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* 1. Encrypted local secret vault (.atabey/secrets.vault.json) using AES-256-GCM
|
|
6
|
+
* with per-secret random salt (HKDF-SHA256) and 96-bit IV.
|
|
7
|
+
* 2. Subprocess environment scrubber (strips sensitive keys before spawning child processes).
|
|
8
|
+
* 3. Secret leak scanner (detects API keys, cloud credentials, private keys, and vault values).
|
|
9
|
+
*/
|
|
10
|
+
import crypto from "crypto";
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { resolveFrameworkDir } from "../storage/storage.js";
|
|
14
|
+
/** Env var names or patterns that must never leak into untrusted subprocesses. */
|
|
15
|
+
const SENSITIVE_ENV_PATTERNS = [
|
|
16
|
+
/_KEY$/i,
|
|
17
|
+
/_KEYS$/i,
|
|
18
|
+
/_SECRET/i,
|
|
19
|
+
/_TOKEN$/i,
|
|
20
|
+
/_PASSWORD$/i,
|
|
21
|
+
/_PASSWD$/i,
|
|
22
|
+
/(^|_)PWD$/i,
|
|
23
|
+
/^PGPASSWORD$/i,
|
|
24
|
+
/_CREDENTIAL/i,
|
|
25
|
+
/_PRIVATE/i,
|
|
26
|
+
/_PASSPHRASE$/i,
|
|
27
|
+
/ACCESS_KEY/i,
|
|
28
|
+
/_APIKEY$/i,
|
|
29
|
+
/_DSN$/i,
|
|
30
|
+
/(^|_)AUTH$/i,
|
|
31
|
+
/_SALT$/i,
|
|
32
|
+
/(^|_)SECRETKEY/i,
|
|
33
|
+
/^ATABEY_AUDIT_HMAC_KEY$/i,
|
|
34
|
+
/^ATABEY_MASTER_KEY$/i,
|
|
35
|
+
/^MCP_AUTH_TOKEN$/i,
|
|
36
|
+
/^MCP_AUTH_USERS$/i,
|
|
37
|
+
/^DATABASE_URL$/i,
|
|
38
|
+
/^REDIS_URL$/i,
|
|
39
|
+
/^OPENAI_API_KEY$/i,
|
|
40
|
+
/^ANTHROPIC_API_KEY$/i,
|
|
41
|
+
/^GEMINI_API_KEY$/i,
|
|
42
|
+
/^GOOGLE_API_KEY$/i,
|
|
43
|
+
/^AWS_SECRET_ACCESS_KEY$/i,
|
|
44
|
+
/^AWS_SESSION_TOKEN$/i,
|
|
45
|
+
/^GITHUB_TOKEN$/i,
|
|
46
|
+
/^GH_TOKEN$/i,
|
|
47
|
+
/^GITLAB_TOKEN$/i,
|
|
48
|
+
/^NPM_TOKEN$/i,
|
|
49
|
+
/^SLACK_BOT_TOKEN$/i,
|
|
50
|
+
/^STRIPE_SECRET_KEY$/i,
|
|
51
|
+
];
|
|
52
|
+
const SECRET_LEAK_PATTERNS = [
|
|
53
|
+
{
|
|
54
|
+
id: "aws-access-key",
|
|
55
|
+
description: "AWS Access Key ID",
|
|
56
|
+
regex: /\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "github-pat",
|
|
60
|
+
description: "GitHub Personal Access Token",
|
|
61
|
+
regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\b|\bgithub_pat_[A-Za-z0-9_]{80,255}\b/g,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "openai-anthropic-key",
|
|
65
|
+
description: "OpenAI / Anthropic API Key",
|
|
66
|
+
regex: /\bsk-(?:proj-|ant-api\d{2}-|live-|test-)?[A-Za-z0-9_-]{20,200}\b/g,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "google-api-key",
|
|
70
|
+
description: "Google Cloud / Gemini API Key",
|
|
71
|
+
regex: /\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "slack-token",
|
|
75
|
+
description: "Slack API Token",
|
|
76
|
+
regex: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*\b/g,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "private-key-pem",
|
|
80
|
+
description: "PEM Private Key Block",
|
|
81
|
+
regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "stripe-secret",
|
|
85
|
+
description: "Stripe Live/Test Secret Key",
|
|
86
|
+
regex: /\b(?:sk|rk)_(?:live|test)_[0-9a-zA-Z]{24,99}\b/g,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
function deriveSubkey(masterKey, salt) {
|
|
90
|
+
return Buffer.from(crypto.hkdfSync("sha256", masterKey, salt, Buffer.from("atabey-vault-v1", "utf8"), 32));
|
|
91
|
+
}
|
|
92
|
+
function redactPreview(match) {
|
|
93
|
+
if (match.length <= 8)
|
|
94
|
+
return "***";
|
|
95
|
+
return `${match.slice(0, 4)}...${match.slice(-3)}`;
|
|
96
|
+
}
|
|
97
|
+
export class SecretManager {
|
|
98
|
+
static resolveVaultDir(customDir) {
|
|
99
|
+
if (customDir)
|
|
100
|
+
return customDir;
|
|
101
|
+
const envDir = process.env.ATABEY_TEST_DIR || process.env.ATABEY_FRAMEWORK_DIR;
|
|
102
|
+
if (envDir && envDir.trim())
|
|
103
|
+
return envDir.trim();
|
|
104
|
+
return resolveFrameworkDir();
|
|
105
|
+
}
|
|
106
|
+
static resolveMasterKey(vaultDir) {
|
|
107
|
+
const envKey = process.env.ATABEY_MASTER_KEY?.trim();
|
|
108
|
+
if (envKey) {
|
|
109
|
+
return crypto.createHash("sha256").update(envKey, "utf8").digest();
|
|
110
|
+
}
|
|
111
|
+
const keyPath = path.join(vaultDir, "master.key");
|
|
112
|
+
if (fs.existsSync(keyPath)) {
|
|
113
|
+
const raw = fs.readFileSync(keyPath);
|
|
114
|
+
if (raw.length === 32)
|
|
115
|
+
return raw;
|
|
116
|
+
return crypto.createHash("sha256").update(raw).digest();
|
|
117
|
+
}
|
|
118
|
+
fs.mkdirSync(vaultDir, { recursive: true });
|
|
119
|
+
const generated = crypto.randomBytes(32);
|
|
120
|
+
fs.writeFileSync(keyPath, generated, { mode: 0o600 });
|
|
121
|
+
return generated;
|
|
122
|
+
}
|
|
123
|
+
static vaultFilePath(vaultDir) {
|
|
124
|
+
return path.join(vaultDir, "secrets.vault.json");
|
|
125
|
+
}
|
|
126
|
+
static readVault(vaultDir) {
|
|
127
|
+
const filePath = this.vaultFilePath(vaultDir);
|
|
128
|
+
if (!fs.existsSync(filePath)) {
|
|
129
|
+
return { version: 1, entries: {} };
|
|
130
|
+
}
|
|
131
|
+
const raw = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
132
|
+
if (!raw || raw.version !== 1 || typeof raw.entries !== "object" || raw.entries === null) {
|
|
133
|
+
throw new Error("Invalid or unsupported secrets.vault.json format.");
|
|
134
|
+
}
|
|
135
|
+
return raw;
|
|
136
|
+
}
|
|
137
|
+
static writeVault(vaultDir, vault) {
|
|
138
|
+
fs.mkdirSync(vaultDir, { recursive: true });
|
|
139
|
+
const filePath = this.vaultFilePath(vaultDir);
|
|
140
|
+
const tmpPath = `${filePath}.tmp.${process.pid}`;
|
|
141
|
+
fs.writeFileSync(tmpPath, JSON.stringify(vault, null, 2), { mode: 0o600 });
|
|
142
|
+
fs.renameSync(tmpPath, filePath);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Encrypt and store a secret in the local AES-256-GCM vault.
|
|
146
|
+
*/
|
|
147
|
+
static setSecret(name, value, options = {}) {
|
|
148
|
+
const keyName = name.trim();
|
|
149
|
+
if (!keyName || !/^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/.test(keyName)) {
|
|
150
|
+
throw new Error(`Invalid secret name "${name}". Use alphanumeric, '_', '.', or '-' characters.`);
|
|
151
|
+
}
|
|
152
|
+
const vaultDir = this.resolveVaultDir(options.vaultDir);
|
|
153
|
+
const masterKey = this.resolveMasterKey(vaultDir);
|
|
154
|
+
const salt = crypto.randomBytes(16);
|
|
155
|
+
const iv = crypto.randomBytes(12);
|
|
156
|
+
const subkey = deriveSubkey(masterKey, salt);
|
|
157
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", subkey, iv);
|
|
158
|
+
cipher.setAAD(Buffer.from(keyName, "utf8"));
|
|
159
|
+
const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
160
|
+
const tag = cipher.getAuthTag();
|
|
161
|
+
const vault = this.readVault(vaultDir);
|
|
162
|
+
const now = new Date().toISOString();
|
|
163
|
+
const existing = vault.entries[keyName];
|
|
164
|
+
vault.entries[keyName] = {
|
|
165
|
+
salt: salt.toString("base64"),
|
|
166
|
+
iv: iv.toString("base64"),
|
|
167
|
+
tag: tag.toString("base64"),
|
|
168
|
+
ciphertext: encrypted.toString("base64"),
|
|
169
|
+
createdAt: existing?.createdAt ?? now,
|
|
170
|
+
updatedAt: now,
|
|
171
|
+
description: options.description ?? existing?.description,
|
|
172
|
+
};
|
|
173
|
+
this.writeVault(vaultDir, vault);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Retrieve and decrypt a secret by name.
|
|
177
|
+
* Checks process.env[name] fallback only when allowEnvFallback is true.
|
|
178
|
+
*/
|
|
179
|
+
static getSecret(name, options = {}) {
|
|
180
|
+
const keyName = name.trim();
|
|
181
|
+
const vaultDir = this.resolveVaultDir(options.vaultDir);
|
|
182
|
+
const vault = this.readVault(vaultDir);
|
|
183
|
+
const entry = vault.entries[keyName];
|
|
184
|
+
if (!entry) {
|
|
185
|
+
if (options.allowEnvFallback && process.env[keyName] !== undefined) {
|
|
186
|
+
return process.env[keyName] ?? null;
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const masterKey = this.resolveMasterKey(vaultDir);
|
|
191
|
+
const salt = Buffer.from(entry.salt, "base64");
|
|
192
|
+
const iv = Buffer.from(entry.iv, "base64");
|
|
193
|
+
const tag = Buffer.from(entry.tag, "base64");
|
|
194
|
+
const ciphertext = Buffer.from(entry.ciphertext, "base64");
|
|
195
|
+
const subkey = deriveSubkey(masterKey, salt);
|
|
196
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", subkey, iv);
|
|
197
|
+
decipher.setAAD(Buffer.from(keyName, "utf8"));
|
|
198
|
+
decipher.setAuthTag(tag);
|
|
199
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
200
|
+
return decrypted.toString("utf8");
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Delete a secret from the vault.
|
|
204
|
+
*/
|
|
205
|
+
static deleteSecret(name, options = {}) {
|
|
206
|
+
const keyName = name.trim();
|
|
207
|
+
const vaultDir = this.resolveVaultDir(options.vaultDir);
|
|
208
|
+
const vault = this.readVault(vaultDir);
|
|
209
|
+
if (!(keyName in vault.entries))
|
|
210
|
+
return false;
|
|
211
|
+
delete vault.entries[keyName];
|
|
212
|
+
this.writeVault(vaultDir, vault);
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* List stored secret metadata without exposing plaintext values.
|
|
217
|
+
*/
|
|
218
|
+
static listSecrets(options = {}) {
|
|
219
|
+
const vaultDir = this.resolveVaultDir(options.vaultDir);
|
|
220
|
+
const vault = this.readVault(vaultDir);
|
|
221
|
+
return Object.entries(vault.entries).map(([name, entry]) => ({
|
|
222
|
+
name,
|
|
223
|
+
createdAt: entry.createdAt,
|
|
224
|
+
updatedAt: entry.updatedAt,
|
|
225
|
+
description: entry.description,
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Re-encrypt all vault entries under a new 32-byte master key.
|
|
230
|
+
*
|
|
231
|
+
* Safe ordering: decrypt everything first, build the new vault in memory,
|
|
232
|
+
* then atomically swap. The previous key is kept as `master.key.bak` for
|
|
233
|
+
* the duration of the swap and restored on failure, so a mid-rotation error
|
|
234
|
+
* cannot make the vault permanently undecryptable.
|
|
235
|
+
*/
|
|
236
|
+
static rotateMasterKey(newKeyMaterial, options = {}) {
|
|
237
|
+
const vaultDir = this.resolveVaultDir(options.vaultDir);
|
|
238
|
+
if (process.env.ATABEY_MASTER_KEY?.trim()) {
|
|
239
|
+
throw new Error("[VAULT] ATABEY_MASTER_KEY is authoritative; file-based rotation is disabled while it is set. " +
|
|
240
|
+
"Rotate by changing ATABEY_MASTER_KEY and re-running `atabey secrets set` for each secret.");
|
|
241
|
+
}
|
|
242
|
+
const vault = this.readVault(vaultDir);
|
|
243
|
+
const names = Object.keys(vault.entries);
|
|
244
|
+
if (names.length === 0)
|
|
245
|
+
return 0;
|
|
246
|
+
// 1. Decrypt everything with the CURRENT key (no writes yet).
|
|
247
|
+
const decrypted = names.map((name) => {
|
|
248
|
+
const value = this.getSecret(name, { vaultDir });
|
|
249
|
+
if (value === null) {
|
|
250
|
+
throw new Error(`Failed to decrypt secret "${name}" during key rotation.`);
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name,
|
|
254
|
+
value,
|
|
255
|
+
description: vault.entries[name].description,
|
|
256
|
+
createdAt: vault.entries[name].createdAt,
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
const newMasterKey = typeof newKeyMaterial === "string"
|
|
260
|
+
? crypto.createHash("sha256").update(newKeyMaterial, "utf8").digest()
|
|
261
|
+
: newKeyMaterial.length === 32
|
|
262
|
+
? Buffer.from(newKeyMaterial)
|
|
263
|
+
: crypto.createHash("sha256").update(newKeyMaterial).digest();
|
|
264
|
+
// 2. Build the new vault in memory, encrypted with the new key.
|
|
265
|
+
const newEntries = {};
|
|
266
|
+
const now = new Date().toISOString();
|
|
267
|
+
for (const item of decrypted) {
|
|
268
|
+
const salt = crypto.randomBytes(16);
|
|
269
|
+
const iv = crypto.randomBytes(12);
|
|
270
|
+
const subkey = deriveSubkey(newMasterKey, salt);
|
|
271
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", subkey, iv);
|
|
272
|
+
cipher.setAAD(Buffer.from(item.name, "utf8"));
|
|
273
|
+
const encrypted = Buffer.concat([cipher.update(item.value, "utf8"), cipher.final()]);
|
|
274
|
+
newEntries[item.name] = {
|
|
275
|
+
salt: salt.toString("base64"),
|
|
276
|
+
iv: iv.toString("base64"),
|
|
277
|
+
tag: cipher.getAuthTag().toString("base64"),
|
|
278
|
+
ciphertext: encrypted.toString("base64"),
|
|
279
|
+
createdAt: item.createdAt,
|
|
280
|
+
updatedAt: now,
|
|
281
|
+
description: item.description,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
// 3. Atomically swap key + vault with rollback protection.
|
|
285
|
+
fs.mkdirSync(vaultDir, { recursive: true });
|
|
286
|
+
const keyPath = path.join(vaultDir, "master.key");
|
|
287
|
+
const backupPath = `${keyPath}.bak`;
|
|
288
|
+
const hadKey = fs.existsSync(keyPath);
|
|
289
|
+
const oldKey = hadKey ? fs.readFileSync(keyPath) : null;
|
|
290
|
+
try {
|
|
291
|
+
if (oldKey)
|
|
292
|
+
fs.writeFileSync(backupPath, oldKey, { mode: 0o600 });
|
|
293
|
+
fs.writeFileSync(keyPath, newMasterKey, { mode: 0o600 });
|
|
294
|
+
this.writeVault(vaultDir, { version: 1, entries: newEntries });
|
|
295
|
+
if (fs.existsSync(backupPath))
|
|
296
|
+
fs.unlinkSync(backupPath);
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
// Restore the previous key so the (unchanged) old vault stays readable.
|
|
300
|
+
try {
|
|
301
|
+
if (oldKey)
|
|
302
|
+
fs.writeFileSync(keyPath, oldKey, { mode: 0o600 });
|
|
303
|
+
else if (fs.existsSync(keyPath))
|
|
304
|
+
fs.unlinkSync(keyPath);
|
|
305
|
+
if (fs.existsSync(backupPath))
|
|
306
|
+
fs.unlinkSync(backupPath);
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
/* best-effort rollback */
|
|
310
|
+
}
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
313
|
+
return decrypted.length;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Scrub sensitive credentials from an environment object before passing to a child process.
|
|
318
|
+
*/
|
|
319
|
+
export function scrubEnvForSubprocess(env = process.env, extraAllowedKeys = []) {
|
|
320
|
+
const allowed = new Set(extraAllowedKeys.map((k) => k.toUpperCase()));
|
|
321
|
+
const clean = {};
|
|
322
|
+
for (const [key, value] of Object.entries(env)) {
|
|
323
|
+
if (value === undefined)
|
|
324
|
+
continue;
|
|
325
|
+
if (allowed.has(key.toUpperCase())) {
|
|
326
|
+
clean[key] = value;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const isSensitive = SENSITIVE_ENV_PATTERNS.some((re) => re.test(key));
|
|
330
|
+
if (!isSensitive) {
|
|
331
|
+
clean[key] = value;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return clean;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Scan arbitrary text (command output, file content, tool arguments) for leaked secrets.
|
|
338
|
+
*/
|
|
339
|
+
export function scanForSecrets(text) {
|
|
340
|
+
if (!text)
|
|
341
|
+
return [];
|
|
342
|
+
const findings = [];
|
|
343
|
+
for (const pattern of SECRET_LEAK_PATTERNS) {
|
|
344
|
+
pattern.regex.lastIndex = 0;
|
|
345
|
+
let match;
|
|
346
|
+
while ((match = pattern.regex.exec(text)) !== null) {
|
|
347
|
+
findings.push({
|
|
348
|
+
ruleId: pattern.id,
|
|
349
|
+
description: pattern.description,
|
|
350
|
+
matchPreview: redactPreview(match[0]),
|
|
351
|
+
index: match.index,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return findings;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Redact leaked secrets from text before it is returned to an AI/LLM.
|
|
359
|
+
* Complements PII masking with cloud-credential/private-key patterns.
|
|
360
|
+
*/
|
|
361
|
+
export function redactSecrets(text) {
|
|
362
|
+
if (!text)
|
|
363
|
+
return text;
|
|
364
|
+
let out = text;
|
|
365
|
+
for (const pattern of SECRET_LEAK_PATTERNS) {
|
|
366
|
+
const flags = pattern.regex.flags.includes("g") ? pattern.regex.flags : `${pattern.regex.flags}g`;
|
|
367
|
+
out = out.replace(new RegExp(pattern.regex.source, flags), `[REDACTED:${pattern.id}]`);
|
|
368
|
+
}
|
|
369
|
+
return out;
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../../../../shared/src/modules/security/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS 1.3 / mTLS transport security for Atabey HTTP + WebSocket servers.
|
|
3
|
+
*
|
|
4
|
+
* All network-facing servers (MCP unified server) go through
|
|
5
|
+
* `createHttpOrHttpsServer()` so encryption is applied uniformly.
|
|
6
|
+
*
|
|
7
|
+
* Environment:
|
|
8
|
+
* ATABEY_TLS_ENABLED=auto|true|false (auto = enabled when cert+key set)
|
|
9
|
+
* ATABEY_TLS_CERT_FILE=/path/cert.pem (server certificate, PEM)
|
|
10
|
+
* ATABEY_TLS_KEY_FILE=/path/key.pem (server private key, PEM)
|
|
11
|
+
* ATABEY_TLS_CA_FILE=/path/ca.pem (CA bundle; required for mTLS)
|
|
12
|
+
* ATABEY_TLS_MIN_VERSION=TLSv1.3 (TLSv1.3 | TLSv1.2)
|
|
13
|
+
* ATABEY_MTLS_REQUIRED=true (require + verify client certificates)
|
|
14
|
+
* ATABEY_TLS_REQUIRED=true (fail hard if TLS not configured)
|
|
15
|
+
* ATABEY_ALLOW_INSECURE_BIND=true (allow plaintext on non-loopback host)
|
|
16
|
+
*
|
|
17
|
+
* Cipher suites are left to Node's secure defaults, which already prefer
|
|
18
|
+
* TLS 1.3 (AES-256-GCM / CHACHA20-POLY1305). We additionally enforce a modern
|
|
19
|
+
* minimum version and disable session tickets for HITL-sensitive traffic.
|
|
20
|
+
*/
|
|
21
|
+
import http from "http";
|
|
22
|
+
import https from "https";
|
|
23
|
+
export type TlsMinVersion = "TLSv1.2" | "TLSv1.3";
|
|
24
|
+
export interface ResolvedTlsConfig {
|
|
25
|
+
/** Whether the server will actually listen over TLS. */
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
minVersion: TlsMinVersion;
|
|
28
|
+
mtlsRequired: boolean;
|
|
29
|
+
/** ATABEY_TLS_REQUIRED — hard-fail when TLS is not configured. */
|
|
30
|
+
required: boolean;
|
|
31
|
+
certFile?: string;
|
|
32
|
+
keyFile?: string;
|
|
33
|
+
caFile?: string;
|
|
34
|
+
/** Present only when enabled. */
|
|
35
|
+
serverOptions?: https.ServerOptions;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the TLS configuration from environment. Throws when TLS is enabled
|
|
39
|
+
* but the certificate material is missing/invalid, or when mTLS is required
|
|
40
|
+
* without a CA bundle.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveTlsConfig(): ResolvedTlsConfig;
|
|
43
|
+
/**
|
|
44
|
+
* Outbound (client) TLS options for HTTPS requests made by Atabey tooling.
|
|
45
|
+
*
|
|
46
|
+
* Environment:
|
|
47
|
+
* ATABEY_TLS_CLIENT_CERT_FILE=/path/client.crt (mTLS client certificate)
|
|
48
|
+
* ATABEY_TLS_CLIENT_KEY_FILE=/path/client.key (mTLS client key)
|
|
49
|
+
* ATABEY_TLS_CLIENT_CA_FILE=/path/ca.pem (custom CA bundle)
|
|
50
|
+
* ATABEY_TLS_CLIENT_MIN_VERSION=TLSv1.3
|
|
51
|
+
* ATABEY_TLS_CLIENT_SERVERNAME=api.example.com (optional SNI override)
|
|
52
|
+
*/
|
|
53
|
+
export interface OutboundTlsConfig {
|
|
54
|
+
enabled: boolean;
|
|
55
|
+
minVersion: TlsMinVersion;
|
|
56
|
+
options: {
|
|
57
|
+
cert?: Buffer;
|
|
58
|
+
key?: Buffer;
|
|
59
|
+
ca?: Buffer;
|
|
60
|
+
minVersion?: TlsMinVersion;
|
|
61
|
+
servername?: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export declare function resolveOutboundTlsOptions(): OutboundTlsConfig;
|
|
65
|
+
/**
|
|
66
|
+
* Create an HTTPS server when TLS is configured, otherwise a plain HTTP server.
|
|
67
|
+
* Both server types satisfy `http.Server`, so callers stay type-safe.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createHttpOrHttpsServer(tls: ResolvedTlsConfig, handler: http.RequestListener): http.Server;
|
|
70
|
+
/**
|
|
71
|
+
* Human-readable description for logs / `/mcp/health`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeTls(tls: ResolvedTlsConfig): string;
|
|
74
|
+
/**
|
|
75
|
+
* Enforce the transport-security policy before binding.
|
|
76
|
+
*
|
|
77
|
+
* - If ATABEY_TLS_REQUIRED=true and TLS is not configured → throw (fail closed).
|
|
78
|
+
* - If binding to a non-loopback host over plaintext → throw unless
|
|
79
|
+
* ATABEY_ALLOW_INSECURE_BIND=true (prevents accidental public plaintext).
|
|
80
|
+
*/
|
|
81
|
+
export declare function assertTransportSecurity(tls: ResolvedTlsConfig, host: string, opts?: {
|
|
82
|
+
allowInsecureBind?: boolean;
|
|
83
|
+
}): void;
|
|
84
|
+
/** The scheme clients should use to reach this server. */
|
|
85
|
+
export declare function tlsScheme(tls: ResolvedTlsConfig): "https" | "http";
|
|
86
|
+
/** The WebSocket scheme clients should use. */
|
|
87
|
+
export declare function wsScheme(tls: ResolvedTlsConfig): "wss" | "ws";
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS 1.3 / mTLS transport security for Atabey HTTP + WebSocket servers.
|
|
3
|
+
*
|
|
4
|
+
* All network-facing servers (MCP unified server) go through
|
|
5
|
+
* `createHttpOrHttpsServer()` so encryption is applied uniformly.
|
|
6
|
+
*
|
|
7
|
+
* Environment:
|
|
8
|
+
* ATABEY_TLS_ENABLED=auto|true|false (auto = enabled when cert+key set)
|
|
9
|
+
* ATABEY_TLS_CERT_FILE=/path/cert.pem (server certificate, PEM)
|
|
10
|
+
* ATABEY_TLS_KEY_FILE=/path/key.pem (server private key, PEM)
|
|
11
|
+
* ATABEY_TLS_CA_FILE=/path/ca.pem (CA bundle; required for mTLS)
|
|
12
|
+
* ATABEY_TLS_MIN_VERSION=TLSv1.3 (TLSv1.3 | TLSv1.2)
|
|
13
|
+
* ATABEY_MTLS_REQUIRED=true (require + verify client certificates)
|
|
14
|
+
* ATABEY_TLS_REQUIRED=true (fail hard if TLS not configured)
|
|
15
|
+
* ATABEY_ALLOW_INSECURE_BIND=true (allow plaintext on non-loopback host)
|
|
16
|
+
*
|
|
17
|
+
* Cipher suites are left to Node's secure defaults, which already prefer
|
|
18
|
+
* TLS 1.3 (AES-256-GCM / CHACHA20-POLY1305). We additionally enforce a modern
|
|
19
|
+
* minimum version and disable session tickets for HITL-sensitive traffic.
|
|
20
|
+
*/
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
import http from "http";
|
|
23
|
+
import https from "https";
|
|
24
|
+
function envBool(key, fallback = false) {
|
|
25
|
+
const v = process.env[key];
|
|
26
|
+
if (v === undefined)
|
|
27
|
+
return fallback;
|
|
28
|
+
return v === "true" || v === "1" || v === "yes";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the TLS configuration from environment. Throws when TLS is enabled
|
|
32
|
+
* but the certificate material is missing/invalid, or when mTLS is required
|
|
33
|
+
* without a CA bundle.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveTlsConfig() {
|
|
36
|
+
const certFile = process.env.ATABEY_TLS_CERT_FILE?.trim() || undefined;
|
|
37
|
+
const keyFile = process.env.ATABEY_TLS_KEY_FILE?.trim() || undefined;
|
|
38
|
+
const caFile = process.env.ATABEY_TLS_CA_FILE?.trim() || undefined;
|
|
39
|
+
const mtlsRequired = envBool("ATABEY_MTLS_REQUIRED", false);
|
|
40
|
+
const required = envBool("ATABEY_TLS_REQUIRED", false);
|
|
41
|
+
const minVersion = (process.env.ATABEY_TLS_MIN_VERSION?.trim() || "TLSv1.3");
|
|
42
|
+
if (minVersion !== "TLSv1.2" && minVersion !== "TLSv1.3") {
|
|
43
|
+
throw new Error(`[TLS] Invalid ATABEY_TLS_MIN_VERSION "${minVersion}" (use TLSv1.2 or TLSv1.3).`);
|
|
44
|
+
}
|
|
45
|
+
const hasKeyPair = Boolean(certFile && keyFile);
|
|
46
|
+
const explicit = process.env.ATABEY_TLS_ENABLED;
|
|
47
|
+
const enabled = explicit === undefined ? hasKeyPair : envBool("ATABEY_TLS_ENABLED", hasKeyPair);
|
|
48
|
+
if (!enabled) {
|
|
49
|
+
return { enabled: false, minVersion, mtlsRequired, required, certFile, keyFile, caFile };
|
|
50
|
+
}
|
|
51
|
+
if (!certFile || !keyFile) {
|
|
52
|
+
throw new Error("[TLS] ATABEY_TLS_ENABLED is set but ATABEY_TLS_CERT_FILE / ATABEY_TLS_KEY_FILE are missing.");
|
|
53
|
+
}
|
|
54
|
+
for (const f of [certFile, keyFile]) {
|
|
55
|
+
if (!fs.existsSync(f))
|
|
56
|
+
throw new Error(`[TLS] Certificate file not found: ${f}`);
|
|
57
|
+
}
|
|
58
|
+
if (mtlsRequired && !caFile) {
|
|
59
|
+
throw new Error("[TLS] ATABEY_MTLS_REQUIRED=true requires ATABEY_TLS_CA_FILE (client CA bundle).");
|
|
60
|
+
}
|
|
61
|
+
if (caFile && !fs.existsSync(caFile)) {
|
|
62
|
+
throw new Error(`[TLS] CA file not found: ${caFile}`);
|
|
63
|
+
}
|
|
64
|
+
const serverOptions = {
|
|
65
|
+
minVersion,
|
|
66
|
+
cert: fs.readFileSync(certFile),
|
|
67
|
+
key: fs.readFileSync(keyFile),
|
|
68
|
+
honorCipherOrder: true,
|
|
69
|
+
// Avoid stale TLS sessions after key rotation.
|
|
70
|
+
sessionTimeout: 0,
|
|
71
|
+
};
|
|
72
|
+
if (caFile)
|
|
73
|
+
serverOptions.ca = fs.readFileSync(caFile);
|
|
74
|
+
if (mtlsRequired) {
|
|
75
|
+
serverOptions.requestCert = true;
|
|
76
|
+
serverOptions.rejectUnauthorized = true;
|
|
77
|
+
}
|
|
78
|
+
return { enabled: true, minVersion, mtlsRequired, required, certFile, keyFile, caFile, serverOptions };
|
|
79
|
+
}
|
|
80
|
+
export function resolveOutboundTlsOptions() {
|
|
81
|
+
const certFile = process.env.ATABEY_TLS_CLIENT_CERT_FILE?.trim() || undefined;
|
|
82
|
+
const keyFile = process.env.ATABEY_TLS_CLIENT_KEY_FILE?.trim() || undefined;
|
|
83
|
+
const caFile = process.env.ATABEY_TLS_CLIENT_CA_FILE?.trim() || undefined;
|
|
84
|
+
const minVersion = (process.env.ATABEY_TLS_CLIENT_MIN_VERSION?.trim() || "TLSv1.3");
|
|
85
|
+
const servername = process.env.ATABEY_TLS_CLIENT_SERVERNAME?.trim() || undefined;
|
|
86
|
+
if (minVersion !== "TLSv1.2" && minVersion !== "TLSv1.3") {
|
|
87
|
+
throw new Error(`[TLS] Invalid ATABEY_TLS_CLIENT_MIN_VERSION "${minVersion}".`);
|
|
88
|
+
}
|
|
89
|
+
if ((certFile && !keyFile) || (keyFile && !certFile)) {
|
|
90
|
+
throw new Error("[TLS] Outbound mTLS requires BOTH ATABEY_TLS_CLIENT_CERT_FILE and ATABEY_TLS_CLIENT_KEY_FILE.");
|
|
91
|
+
}
|
|
92
|
+
for (const f of [certFile, keyFile, caFile]) {
|
|
93
|
+
if (f && !fs.existsSync(f))
|
|
94
|
+
throw new Error(`[TLS] Outbound certificate file not found: ${f}`);
|
|
95
|
+
}
|
|
96
|
+
const enabled = Boolean(certFile || keyFile || caFile);
|
|
97
|
+
const options = { minVersion };
|
|
98
|
+
if (certFile)
|
|
99
|
+
options.cert = fs.readFileSync(certFile);
|
|
100
|
+
if (keyFile)
|
|
101
|
+
options.key = fs.readFileSync(keyFile);
|
|
102
|
+
if (caFile)
|
|
103
|
+
options.ca = fs.readFileSync(caFile);
|
|
104
|
+
if (servername)
|
|
105
|
+
options.servername = servername;
|
|
106
|
+
return { enabled, minVersion, options };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create an HTTPS server when TLS is configured, otherwise a plain HTTP server.
|
|
110
|
+
* Both server types satisfy `http.Server`, so callers stay type-safe.
|
|
111
|
+
*/
|
|
112
|
+
export function createHttpOrHttpsServer(tls, handler) {
|
|
113
|
+
if (tls.enabled && tls.serverOptions) {
|
|
114
|
+
return https.createServer(tls.serverOptions, handler);
|
|
115
|
+
}
|
|
116
|
+
return http.createServer(handler);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Human-readable description for logs / `/mcp/health`.
|
|
120
|
+
*/
|
|
121
|
+
export function describeTls(tls) {
|
|
122
|
+
if (!tls.enabled)
|
|
123
|
+
return "disabled (plaintext)";
|
|
124
|
+
return `${tls.minVersion}${tls.mtlsRequired ? " + mTLS (client cert required)" : ""}`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Enforce the transport-security policy before binding.
|
|
128
|
+
*
|
|
129
|
+
* - If ATABEY_TLS_REQUIRED=true and TLS is not configured → throw (fail closed).
|
|
130
|
+
* - If binding to a non-loopback host over plaintext → throw unless
|
|
131
|
+
* ATABEY_ALLOW_INSECURE_BIND=true (prevents accidental public plaintext).
|
|
132
|
+
*/
|
|
133
|
+
export function assertTransportSecurity(tls, host, opts = {}) {
|
|
134
|
+
if (tls.required && !tls.enabled) {
|
|
135
|
+
throw new Error("[TLS] ATABEY_TLS_REQUIRED=true but TLS is not configured. " +
|
|
136
|
+
"Provide ATABEY_TLS_CERT_FILE / ATABEY_TLS_KEY_FILE (and ATABEY_TLS_CA_FILE for mTLS).");
|
|
137
|
+
}
|
|
138
|
+
const normalized = host.replace(/^\[|\]$/g, "");
|
|
139
|
+
const isLoopback = normalized === "127.0.0.1" || normalized === "::1" || normalized === "localhost";
|
|
140
|
+
const allowInsecure = opts.allowInsecureBind ?? envBool("ATABEY_ALLOW_INSECURE_BIND", false);
|
|
141
|
+
if (!tls.enabled && !isLoopback && !allowInsecure) {
|
|
142
|
+
throw new Error(`[TLS] Refusing to bind plaintext HTTP to "${host}" (non-loopback). ` +
|
|
143
|
+
"Configure TLS (ATABEY_TLS_CERT_FILE/KEY_FILE) or set ATABEY_ALLOW_INSECURE_BIND=true.");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** The scheme clients should use to reach this server. */
|
|
147
|
+
export function tlsScheme(tls) {
|
|
148
|
+
return tls.enabled ? "https" : "http";
|
|
149
|
+
}
|
|
150
|
+
/** The WebSocket scheme clients should use. */
|
|
151
|
+
export function wsScheme(tls) {
|
|
152
|
+
return tls.enabled ? "wss" : "ws";
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=tls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.js","sourceRoot":"","sources":["../../../../../../shared/src/modules/security/tls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
let globalDB = null;
|
|
2
|
+
export const databaseHolder = {
|
|
3
|
+
setDB(db) {
|
|
4
|
+
globalDB = db;
|
|
5
|
+
},
|
|
6
|
+
getDB() {
|
|
7
|
+
if (!globalDB) {
|
|
8
|
+
throw new Error("Atabey Shared Database has not been initialized. Please call databaseHolder.setDB(db) at startup.");
|
|
9
|
+
}
|
|
10
|
+
return globalDB;
|
|
11
|
+
},
|
|
12
|
+
hasDB() {
|
|
13
|
+
return globalDB !== null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=database.js.map
|