enigma-memory 0.1.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/LICENSE +55 -0
- package/README.md +379 -0
- package/apps/cli/bin/enigma.mjs +1117 -0
- package/apps/desktop/src/app.js +1327 -0
- package/apps/desktop/src/index.html +1373 -0
- package/apps/desktop/src/styles.css +386 -0
- package/apps/gateway/bin/enigma-gateway.mjs +52 -0
- package/apps/gateway/src/server.mjs +1403 -0
- package/apps/native-host/README.md +265 -0
- package/apps/native-host/bin/enigma-native-host.mjs +499 -0
- package/apps/native-host/manifests/com.enigma.native_host.chrome.json +9 -0
- package/apps/native-host/manifests/com.enigma.native_host.edge.json +9 -0
- package/apps/native-host/manifests/com.enigma.native_host.firefox.json +9 -0
- package/apps/relay/bin/enigma-relay.mjs +52 -0
- package/apps/relay/src/server.mjs +1571 -0
- package/apps/verifier/bin/enigma-verify.mjs +240 -0
- package/deploy/docker-compose.production.example.yml +150 -0
- package/deploy/kubernetes/enigma-backend.example.yaml +580 -0
- package/package.json +196 -0
- package/packages/adapters/PACKAGE_CONTRACT.md +47 -0
- package/packages/adapters/src/index.js +183 -0
- package/packages/boundary/src/index.js +468 -0
- package/packages/connectors/src/index.js +533 -0
- package/packages/core/src/index.js +766 -0
- package/packages/enterprise/src/index.js +505 -0
- package/packages/importers/src/index.js +1083 -0
- package/packages/mcp-server/bin/enigma-mcp.mjs +19 -0
- package/packages/mcp-server/src/index.js +1185 -0
- package/packages/mesh/src/index.js +531 -0
- package/packages/metering/PACKAGE_CONTRACT.md +20 -0
- package/packages/metering/src/index.js +246 -0
- package/packages/optimizer/src/index.js +707 -0
- package/packages/passport/src/index.js +651 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +28 -0
- package/packages/settlement/src/index.js +369 -0
- package/packages/storage/PACKAGE_CONTRACT.md +23 -0
- package/packages/storage/src/index.js +392 -0
- package/packages/vault/src/index.js +904 -0
- package/scripts/build-ai-orchestration-plan.mjs +248 -0
- package/scripts/build-cloudflare-pages-release-packet.mjs +287 -0
- package/scripts/build-cloudflare-token-policy.mjs +239 -0
- package/scripts/build-cloudflare-token-request.mjs +314 -0
- package/scripts/build-demo-assets.mjs +263 -0
- package/scripts/build-edge-backend-workers.mjs +353 -0
- package/scripts/build-goal-completion-audit.mjs +398 -0
- package/scripts/build-hosted-probe-worker.mjs +191 -0
- package/scripts/build-operator-acceptance-packet.mjs +254 -0
- package/scripts/build-operator-evidence-starter.mjs +525 -0
- package/scripts/build-production-backend-env-kit.mjs +384 -0
- package/scripts/build-production-dependency-report.mjs +369 -0
- package/scripts/build-production-handoff-packet.mjs +475 -0
- package/scripts/build-production-readiness-manifest.mjs +209 -0
- package/scripts/build-production-status-board.mjs +297 -0
- package/scripts/build-production-storage-migration.mjs +86 -0
- package/scripts/build-production-workplan.mjs +371 -0
- package/scripts/build-review-packet.mjs +594 -0
- package/scripts/check.mjs +419 -0
- package/scripts/cloudflare-ops.mjs +1672 -0
- package/scripts/cloudflare-secret-env.mjs +128 -0
- package/scripts/collect-hosted-backend-live-evidence.mjs +263 -0
- package/scripts/infrastructure-readiness.mjs +944 -0
- package/scripts/install-enigma-local.mjs +270 -0
- package/scripts/memory-optimization-benchmark.mjs +242 -0
- package/scripts/monitor-live-endpoints.mjs +358 -0
- package/scripts/release-audit.mjs +5588 -0
- package/scripts/release-provenance.mjs +250 -0
- package/scripts/run-backend-readiness-smoke.mjs +220 -0
- package/scripts/validate-backup-restore-drill.mjs +166 -0
- package/scripts/validate-cloudflare-credentials.mjs +112 -0
- package/scripts/validate-cloudflare-worker-inspect.mjs +179 -0
- package/scripts/validate-domain-tls.mjs +248 -0
- package/scripts/validate-hosted-backend-live.mjs +319 -0
- package/scripts/validate-incident-drill.mjs +221 -0
- package/scripts/validate-kms-custody.mjs +235 -0
- package/scripts/validate-legal-compliance-approval.mjs +274 -0
- package/scripts/validate-monitoring-alerting.mjs +263 -0
- package/scripts/validate-network-access-policy.mjs +285 -0
- package/scripts/validate-operator-acceptance.mjs +298 -0
- package/scripts/validate-production-manifests.mjs +325 -0
- package/scripts/validate-public-site-security.mjs +252 -0
- package/scripts/validate-security-threat-model.mjs +285 -0
- package/scripts/validate-service-settlement.mjs +256 -0
- package/scripts/validate-support-sla.mjs +190 -0
- package/scripts/validate-tenant-policy-approval.mjs +228 -0
- package/scripts/validate-usage-metering.mjs +224 -0
- package/scripts/validate-whitepaper-claims.mjs +166 -0
- package/specs/boundary-manifest-v1.schema.json +35 -0
- package/specs/capsule-v1.schema.json +55 -0
- package/specs/claim-boundary-manifest-v1.schema.json +22 -0
- package/specs/connector-profile-v1.schema.json +62 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -0
- package/specs/enterprise-policy-v1.schema.json +59 -0
- package/specs/gateway-request-v1.schema.json +32 -0
- package/specs/goal-completion-audit-v1.schema.json +70 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +147 -0
- package/specs/import-report-v1.schema.json +214 -0
- package/specs/infrastructure-readiness-manifest-v1.schema.json +130 -0
- package/specs/memory-event-v1.schema.json +42 -0
- package/specs/mesh-node-v1.schema.json +126 -0
- package/specs/passport-v1.schema.json +50 -0
- package/specs/production-handoff-packet-v1.schema.json +149 -0
- package/specs/receipt-v1.schema.json +61 -0
- package/specs/relay-record-v1.schema.json +43 -0
- package/specs/state-checkpoint-v1.schema.json +37 -0
- package/specs/trust-bundle-v1.schema.json +56 -0
|
@@ -0,0 +1,1403 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import {
|
|
4
|
+
createEnterprisePolicy,
|
|
5
|
+
createGatewayDecision,
|
|
6
|
+
evaluateEnterprisePolicy,
|
|
7
|
+
exportSiemEvent,
|
|
8
|
+
minimizeEnterpriseEvaluation,
|
|
9
|
+
minimizeEnterprisePolicy,
|
|
10
|
+
verifyGatewayDecision,
|
|
11
|
+
} from '../../../packages/enterprise/src/index.js';
|
|
12
|
+
import { canonicalize, generateSigningKeyPair, sha256Hex, signPayload, verifySignature } from '../../../packages/core/src/index.js';
|
|
13
|
+
import {
|
|
14
|
+
normalizeDependencyEvidence,
|
|
15
|
+
readinessEvidenceRefs,
|
|
16
|
+
} from '../../../packages/adapters/src/index.js';
|
|
17
|
+
|
|
18
|
+
const GATEWAY_REQUEST_SCHEMA = 'enigma.gateway_request.v1';
|
|
19
|
+
const GATEWAY_EXPORT_SCHEMA = 'enigma.gateway_siem_export.v1';
|
|
20
|
+
const GATEWAY_STATE_SCHEMA = 'enigma.gateway_state.v1';
|
|
21
|
+
const GATEWAY_STATE_VERSION = 1;
|
|
22
|
+
const DEFAULT_GATEWAY_ID = 'gateway_local';
|
|
23
|
+
const DEFAULT_ACTIVE_ROOT = `sha256:${sha256Hex('enigma.gateway.empty_active_root.v1')}`;
|
|
24
|
+
const DEFAULT_BODY_LIMIT = 64 * 1024;
|
|
25
|
+
const JSON_HEADERS = Object.freeze({ 'content-type': 'application/json; charset=utf-8' });
|
|
26
|
+
const LOCAL_READINESS_MODES = new Set(['local', 'demo', 'development', 'test']);
|
|
27
|
+
const PRODUCTION_READINESS_MODES = new Set(['production', 'hosted', 'byoc', 'customer_byoc', 'on_prem', 'onprem']);
|
|
28
|
+
const LOCAL_STORAGE_KINDS = new Set(['memory', 'in_memory', 'in-memory', 'local', 'file', 'state_file', 'state-file', 'json']);
|
|
29
|
+
const DURABLE_STORAGE_KINDS = new Set(['postgres', 'postgresql', 'mysql', 'mariadb', 'database', 'dynamodb', 'r2', 's3', 'gcs', 'azure_blob', 'blob', 'kv', 'foundationdb']);
|
|
30
|
+
const PLAINTEXT_MEMORY_KEYS = new Set([
|
|
31
|
+
'plaintext',
|
|
32
|
+
'plaintextmemory',
|
|
33
|
+
'plainmemory',
|
|
34
|
+
'cleartext',
|
|
35
|
+
'rawmemory',
|
|
36
|
+
'memoryplaintext',
|
|
37
|
+
'memory',
|
|
38
|
+
'memories',
|
|
39
|
+
'text',
|
|
40
|
+
'content',
|
|
41
|
+
'body',
|
|
42
|
+
'prompt',
|
|
43
|
+
'prompttext',
|
|
44
|
+
'completion',
|
|
45
|
+
'message',
|
|
46
|
+
'messagetext',
|
|
47
|
+
'conversation',
|
|
48
|
+
'conversationtext',
|
|
49
|
+
'transcript',
|
|
50
|
+
'transcripttext'
|
|
51
|
+
]);
|
|
52
|
+
const POLICY_ARRAY_FIELDS = [
|
|
53
|
+
'allowed_operations',
|
|
54
|
+
'allowed_providers',
|
|
55
|
+
'allowed_models',
|
|
56
|
+
'allowed_regions',
|
|
57
|
+
'denied_sensitivities',
|
|
58
|
+
'allowed_purposes',
|
|
59
|
+
'legal_holds',
|
|
60
|
+
];
|
|
61
|
+
const SHARED_PRODUCTION_DEPENDENCY_ALIASES = Object.freeze({
|
|
62
|
+
network_access_policy: {
|
|
63
|
+
options: ['network_access_policy', 'networkAccessPolicy', 'network_policy', 'networkPolicy', 'network_access_policy_ref', 'networkAccessPolicyRef'],
|
|
64
|
+
env: ['ENIGMA_NETWORK_ACCESS_POLICY_REF', 'ENIGMA_NETWORK_POLICY_REF'],
|
|
65
|
+
},
|
|
66
|
+
kms_custody: {
|
|
67
|
+
options: ['kms_custody', 'kmsCustody', 'key_custody', 'keyCustody', 'kms_custody_ref', 'kmsCustodyRef'],
|
|
68
|
+
env: ['ENIGMA_KMS_CUSTODY_REF', 'ENIGMA_KEY_CUSTODY_REF'],
|
|
69
|
+
},
|
|
70
|
+
tenant_policy_approval: {
|
|
71
|
+
options: ['tenant_policy_approval', 'tenantPolicyApproval', 'tenant_policy', 'tenantPolicy', 'tenant_policy_approval_ref', 'tenantPolicyApprovalRef'],
|
|
72
|
+
env: ['ENIGMA_TENANT_POLICY_APPROVAL_REF', 'ENIGMA_TENANT_POLICY_REF'],
|
|
73
|
+
},
|
|
74
|
+
usage_metering: {
|
|
75
|
+
options: ['usage_metering', 'usageMetering', 'metering', 'metering_ref', 'meteringRef', 'usage_metering_ref', 'usageMeteringRef'],
|
|
76
|
+
env: ['ENIGMA_USAGE_METERING_REF', 'ENIGMA_METERING_REF'],
|
|
77
|
+
},
|
|
78
|
+
service_settlement: {
|
|
79
|
+
options: ['service_settlement', 'serviceSettlement', 'settlement', 'settlement_ref', 'settlementRef', 'service_settlement_ref', 'serviceSettlementRef'],
|
|
80
|
+
env: ['ENIGMA_SERVICE_SETTLEMENT_REF', 'ENIGMA_SETTLEMENT_REF'],
|
|
81
|
+
},
|
|
82
|
+
monitoring_alerting: {
|
|
83
|
+
options: ['monitoring_alerting', 'monitoringAlerting', 'monitoring_alerting_ref', 'monitoringAlertingRef'],
|
|
84
|
+
env: ['ENIGMA_MONITORING_ALERTING_REF'],
|
|
85
|
+
},
|
|
86
|
+
public_site_security: {
|
|
87
|
+
options: ['public_site_security', 'publicSiteSecurity', 'public_site_security_ref', 'publicSiteSecurityRef'],
|
|
88
|
+
env: ['ENIGMA_PUBLIC_SITE_SECURITY_REF', 'ENIGMA_SITE_SECURITY_REF'],
|
|
89
|
+
},
|
|
90
|
+
security_threat_model: {
|
|
91
|
+
options: ['security_threat_model', 'securityThreatModel', 'threat_model', 'threatModel', 'security_threat_model_ref', 'securityThreatModelRef'],
|
|
92
|
+
env: ['ENIGMA_SECURITY_THREAT_MODEL_REF', 'ENIGMA_THREAT_MODEL_REF'],
|
|
93
|
+
},
|
|
94
|
+
legal_compliance_approval: {
|
|
95
|
+
options: ['legal_compliance_approval', 'legalComplianceApproval', 'legal_compliance', 'legalCompliance', 'legal_compliance_ref', 'legalComplianceRef'],
|
|
96
|
+
env: ['ENIGMA_LEGAL_COMPLIANCE_REF', 'ENIGMA_LEGAL_COMPLIANCE_APPROVAL_REF'],
|
|
97
|
+
},
|
|
98
|
+
support_sla: {
|
|
99
|
+
options: ['support_sla', 'supportSla', 'sla', 'sla_ref', 'slaRef', 'support_sla_ref', 'supportSlaRef'],
|
|
100
|
+
env: ['ENIGMA_SUPPORT_SLA_REF', 'ENIGMA_SLA_REF'],
|
|
101
|
+
},
|
|
102
|
+
incident_drill: {
|
|
103
|
+
options: ['incident_drill', 'incidentDrill', 'incident_drill_ref', 'incidentDrillRef'],
|
|
104
|
+
env: ['ENIGMA_INCIDENT_DRILL_REF', 'ENIGMA_INCIDENT_RESPONSE_DRILL_REF'],
|
|
105
|
+
},
|
|
106
|
+
backup_restore_drill: {
|
|
107
|
+
options: ['backup_restore_drill', 'backupRestoreDrill', 'backup_restore_drill_ref', 'backupRestoreDrillRef'],
|
|
108
|
+
env: ['ENIGMA_BACKUP_RESTORE_DRILL_REF', 'ENIGMA_RESTORE_DRILL_REF'],
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
const SHARED_PRODUCTION_DEPENDENCY_KEYS = Object.freeze(Object.keys(SHARED_PRODUCTION_DEPENDENCY_ALIASES));
|
|
112
|
+
|
|
113
|
+
function isPlainRecord(value) {
|
|
114
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function withoutUndefinedFields(record) {
|
|
118
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
119
|
+
}
|
|
120
|
+
function hashValue(value) {
|
|
121
|
+
return `sha256:${sha256Hex(typeof value === 'string' ? value : canonicalize(value))}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeSha256Digest(value) {
|
|
125
|
+
if (!nonEmptyString(value)) return null;
|
|
126
|
+
const text = value.trim();
|
|
127
|
+
if (/^sha256:[a-f0-9]{64}$/i.test(text)) return `sha256:${text.slice('sha256:'.length).toLowerCase()}`;
|
|
128
|
+
if (/^[a-f0-9]{64}$/i.test(text)) return `sha256:${text.toLowerCase()}`;
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function bearerTokenHash(token) {
|
|
133
|
+
return `sha256:${sha256Hex(String(token))}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function headerValue(headers, name) {
|
|
137
|
+
if (headers === undefined || headers === null) return undefined;
|
|
138
|
+
if (typeof headers.get === 'function') return headers.get(name) ?? headers.get(name.toLowerCase()) ?? undefined;
|
|
139
|
+
if (!isPlainRecord(headers)) return undefined;
|
|
140
|
+
const lower = name.toLowerCase();
|
|
141
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
142
|
+
if (String(key).toLowerCase() !== lower) continue;
|
|
143
|
+
if (Array.isArray(value)) return value[0];
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function bearerTokenFromRequest(request) {
|
|
150
|
+
const authorization = headerValue(request?.headers, 'authorization');
|
|
151
|
+
if (!nonEmptyString(authorization)) return null;
|
|
152
|
+
const match = authorization.trim().match(/^Bearer\s+(.+)$/i);
|
|
153
|
+
return match ? match[1] : null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function normalizeKeyName(key) {
|
|
157
|
+
return String(key).toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function isForbiddenPlaintextKey(key) {
|
|
161
|
+
return PLAINTEXT_MEMORY_KEYS.has(normalizeKeyName(key));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function policyHash(policy) {
|
|
165
|
+
const { policy_hash, policyHash, ...rest } = policy ?? {};
|
|
166
|
+
return hashValue(rest);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
function defaultPolicy(options = {}) {
|
|
172
|
+
return createEnterprisePolicy({
|
|
173
|
+
policy_id: options.policy_id ?? options.policyId ?? 'policy_gateway_default',
|
|
174
|
+
tenant_id: options.tenant_id ?? options.tenantId ?? 'tenant_gateway',
|
|
175
|
+
mode: options.mode ?? 'byoc',
|
|
176
|
+
allowed_providers: options.allowed_providers ?? options.allowedProviders ?? ['anthropic', 'kimi'],
|
|
177
|
+
allowed_models: options.allowed_models ?? options.allowedModels ?? ['claude-3-5-sonnet', 'kimi-k2'],
|
|
178
|
+
allowed_regions: options.allowed_regions ?? options.allowedRegions ?? ['us-east-1', 'eu-central-1'],
|
|
179
|
+
allowed_operations: options.allowed_operations ?? options.allowedOperations ?? ['retrieve', 'remember', 'write', 'delete'],
|
|
180
|
+
allowed_purposes: options.allowed_purposes ?? options.allowedPurposes ?? ['support_retrieval', 'agent_context'],
|
|
181
|
+
denied_sensitivities: options.denied_sensitivities ?? options.deniedSensitivities ?? ['restricted', 'secret'],
|
|
182
|
+
legal_holds: options.legal_holds ?? options.legalHolds ?? [
|
|
183
|
+
{ hold_id: 'hold_gateway_demo', memory_id: 'mem_legal_hold', status: 'active', reason_code: 'LEGAL_HOLD' },
|
|
184
|
+
],
|
|
185
|
+
retention_days: options.retention_days ?? options.retentionDays ?? 365,
|
|
186
|
+
kms: options.kms ?? {
|
|
187
|
+
provider: 'gateway_local_kms',
|
|
188
|
+
key_id: 'gateway-local-demo-key',
|
|
189
|
+
key_version: '1',
|
|
190
|
+
region: 'local',
|
|
191
|
+
},
|
|
192
|
+
now: options.now ?? '1970-01-01T00:00:00.000Z',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function validateGatewayPolicy(policy) {
|
|
197
|
+
const errors = [];
|
|
198
|
+
if (!isPlainRecord(policy)) {
|
|
199
|
+
return { ok: false, errors: ['POLICY_NOT_OBJECT'] };
|
|
200
|
+
}
|
|
201
|
+
if (policy.schema !== 'enigma.enterprise_policy.v1') errors.push('POLICY_SCHEMA_INVALID');
|
|
202
|
+
if (policy.default_action !== 'deny_unknown') errors.push('DEFAULT_DENY_REQUIRED');
|
|
203
|
+
if (policy.provider_native_memory !== 'cache_only') errors.push('PROVIDER_NATIVE_MEMORY_CACHE_ONLY_REQUIRED');
|
|
204
|
+
if (typeof policy.policy_id !== 'string' || policy.policy_id.length === 0) errors.push('POLICY_ID_REQUIRED');
|
|
205
|
+
if (typeof policy.tenant_id !== 'string' || policy.tenant_id.length === 0) errors.push('TENANT_ID_REQUIRED');
|
|
206
|
+
for (const field of POLICY_ARRAY_FIELDS) {
|
|
207
|
+
if (!Array.isArray(policy[field])) errors.push(`${field.toUpperCase()}_ARRAY_REQUIRED`);
|
|
208
|
+
}
|
|
209
|
+
if (!isPlainRecord(policy.kms)) errors.push('KMS_REQUIRED');
|
|
210
|
+
if (typeof policy.policy_hash !== 'string' || !/^sha256:[a-f0-9]{64}$/.test(policy.policy_hash)) {
|
|
211
|
+
errors.push('POLICY_HASH_REQUIRED');
|
|
212
|
+
} else if (policy.policy_hash !== policyHash(policy)) {
|
|
213
|
+
errors.push('POLICY_HASH_MISMATCH');
|
|
214
|
+
}
|
|
215
|
+
return { ok: errors.length === 0, errors };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function containsPlaintextMemory(value, seen = new WeakSet()) {
|
|
219
|
+
if (value === null || typeof value !== 'object') return false;
|
|
220
|
+
if (seen.has(value)) return false;
|
|
221
|
+
seen.add(value);
|
|
222
|
+
if (Array.isArray(value)) return value.some((item) => containsPlaintextMemory(item, seen));
|
|
223
|
+
for (const [key, child] of Object.entries(value)) {
|
|
224
|
+
if (isForbiddenPlaintextKey(key) && child !== undefined && child !== null && child !== '') return true;
|
|
225
|
+
if (containsPlaintextMemory(child, seen)) return true;
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function normalizeGatewayRequest(input = {}) {
|
|
231
|
+
if (!isPlainRecord(input)) {
|
|
232
|
+
return { ok: false, status: 400, errors: ['REQUEST_NOT_OBJECT'] };
|
|
233
|
+
}
|
|
234
|
+
if (containsPlaintextMemory(input)) {
|
|
235
|
+
return { ok: false, status: 400, errors: ['MEMORY_PLAINTEXT_FORBIDDEN'] };
|
|
236
|
+
}
|
|
237
|
+
const source = isPlainRecord(input.request) ? input.request : input;
|
|
238
|
+
if (containsPlaintextMemory(source)) {
|
|
239
|
+
return { ok: false, status: 400, errors: ['MEMORY_PLAINTEXT_FORBIDDEN'] };
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
ok: true,
|
|
243
|
+
request: withoutUndefinedFields({
|
|
244
|
+
schema: source.schema ?? GATEWAY_REQUEST_SCHEMA,
|
|
245
|
+
operation: source.operation,
|
|
246
|
+
provider: source.provider,
|
|
247
|
+
model: source.model,
|
|
248
|
+
region: source.region,
|
|
249
|
+
purpose: source.purpose,
|
|
250
|
+
sensitivity: source.sensitivity,
|
|
251
|
+
memory_addr: source.memory_addr ?? source.memoryAddr,
|
|
252
|
+
memory_id: source.memory_id ?? source.memoryId,
|
|
253
|
+
subject_id: source.subject_id ?? source.subjectId,
|
|
254
|
+
legal_hold_delete: source.legal_hold_delete ?? source.legalHoldDelete,
|
|
255
|
+
}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function unsignedDecision(decision) {
|
|
260
|
+
const { signature, signer, public_key, publicKey, verification_key, verificationKey, ...unsigned } = decision;
|
|
261
|
+
return unsigned;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function redactAndResignDecision(decision, state) {
|
|
265
|
+
const { memory_addr, key_evidence, operation, provider, model, region, purpose, sensitivity, ...redacted } = decision;
|
|
266
|
+
const unsigned = unsignedDecision(redacted);
|
|
267
|
+
return {
|
|
268
|
+
...unsigned,
|
|
269
|
+
signer: redacted.signer,
|
|
270
|
+
signature: {
|
|
271
|
+
alg: 'Ed25519',
|
|
272
|
+
key_id: redacted.signer.key_id,
|
|
273
|
+
value: signPayload(unsigned, state.signingKeyPair.privateKey),
|
|
274
|
+
},
|
|
275
|
+
public_key: redacted.public_key,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function createDecisionForRequest(state, request, evaluation) {
|
|
280
|
+
const decision = createGatewayDecision({
|
|
281
|
+
policy: state.policy,
|
|
282
|
+
request,
|
|
283
|
+
evaluation,
|
|
284
|
+
gateway_id: state.gateway_id,
|
|
285
|
+
active_root: state.active_root,
|
|
286
|
+
signingKeyPair: state.signingKeyPair,
|
|
287
|
+
});
|
|
288
|
+
return redactAndResignDecision(decision, state);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
function publicGatewayEvaluation(state, evaluation) {
|
|
293
|
+
return state.expose_internal === true ? evaluation : minimizeEnterpriseEvaluation(evaluation);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function publicGatewayPolicy(state) {
|
|
297
|
+
return state.expose_internal === true ? state.policy : minimizeEnterprisePolicy(state.policy);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function sanitizeSiemEvent(event) {
|
|
301
|
+
const { operation, provider, model, region, purpose, sensitivity, memory_addr, key_evidence, ...minimized } = event;
|
|
302
|
+
return minimized;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function appendSiemEvent(state, event) {
|
|
306
|
+
const minimized = sanitizeSiemEvent(event);
|
|
307
|
+
state.siem_events.push(minimized);
|
|
308
|
+
return minimized;
|
|
309
|
+
}
|
|
310
|
+
function siemEventForEvaluation(state, evaluation) {
|
|
311
|
+
return appendSiemEvent(state, exportSiemEvent({ policy: state.policy, evaluation }));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function siemEventForDecision(state, decision) {
|
|
315
|
+
return appendSiemEvent(state, exportSiemEvent({ policy: state.policy, decision }));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function eventHasPlaintext(event) {
|
|
319
|
+
if (!isPlainRecord(event)) return true;
|
|
320
|
+
return ['provider', 'model', 'region', 'purpose', 'sensitivity', 'memory_addr', 'key_evidence', 'operation'].some((key) =>
|
|
321
|
+
Object.prototype.hasOwnProperty.call(event, key)
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function failGatewayState(reason) {
|
|
326
|
+
throw new TypeError(`gateway state invalid: ${reason}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function assertSnapshotKeys(record, allowed, label) {
|
|
330
|
+
for (const key of Object.keys(record)) {
|
|
331
|
+
if (!allowed.has(key)) failGatewayState(`${label}_FIELD_FORBIDDEN:${key}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function validateGatewayStateSnapshot(snapshot) {
|
|
336
|
+
if (!isPlainRecord(snapshot)) failGatewayState('SNAPSHOT_NOT_OBJECT');
|
|
337
|
+
assertSnapshotKeys(snapshot, new Set([
|
|
338
|
+
'schema',
|
|
339
|
+
'version',
|
|
340
|
+
'gateway_id',
|
|
341
|
+
'active_root',
|
|
342
|
+
'policy',
|
|
343
|
+
'signing_key',
|
|
344
|
+
'siem_events',
|
|
345
|
+
'expose_internal',
|
|
346
|
+
'generated_at',
|
|
347
|
+
]), 'SNAPSHOT');
|
|
348
|
+
if (snapshot.schema !== GATEWAY_STATE_SCHEMA) failGatewayState('SNAPSHOT_SCHEMA_INVALID');
|
|
349
|
+
if (snapshot.version !== GATEWAY_STATE_VERSION) failGatewayState('SNAPSHOT_VERSION_INVALID');
|
|
350
|
+
if (typeof snapshot.gateway_id !== 'string' || snapshot.gateway_id.length === 0) failGatewayState('GATEWAY_ID_REQUIRED');
|
|
351
|
+
if (typeof snapshot.active_root !== 'string' || !/^sha256:[a-f0-9]{64}$/.test(snapshot.active_root)) failGatewayState('ACTIVE_ROOT_INVALID');
|
|
352
|
+
if (typeof snapshot.generated_at !== 'string' || Number.isNaN(Date.parse(snapshot.generated_at))) failGatewayState('GENERATED_AT_INVALID');
|
|
353
|
+
if (typeof snapshot.expose_internal !== 'boolean') failGatewayState('EXPOSE_INTERNAL_BOOLEAN_REQUIRED');
|
|
354
|
+
if (containsPlaintextMemory(snapshot)) failGatewayState('SNAPSHOT_PLAINTEXT_FORBIDDEN');
|
|
355
|
+
const policyValidation = validateGatewayPolicy(snapshot.policy);
|
|
356
|
+
if (!policyValidation.ok) failGatewayState(`POLICY_INVALID:${policyValidation.errors.join(',')}`);
|
|
357
|
+
validateGatewaySigningKey(snapshot.signing_key);
|
|
358
|
+
validateGatewaySiemEvents(snapshot.siem_events);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function validateGatewaySigningKey(signingKey) {
|
|
362
|
+
if (!isPlainRecord(signingKey)) failGatewayState('SIGNING_KEY_NOT_OBJECT');
|
|
363
|
+
assertSnapshotKeys(signingKey, new Set(['alg', 'key_id', 'public_key', 'private_key']), 'SIGNING_KEY');
|
|
364
|
+
if (signingKey.alg !== 'Ed25519') failGatewayState('SIGNING_KEY_ALG_INVALID');
|
|
365
|
+
if (typeof signingKey.key_id !== 'string' || signingKey.key_id.length === 0) failGatewayState('SIGNING_KEY_ID_REQUIRED');
|
|
366
|
+
if (typeof signingKey.public_key !== 'string' || !signingKey.public_key.includes('BEGIN PUBLIC KEY')) failGatewayState('PUBLIC_KEY_INVALID');
|
|
367
|
+
if (typeof signingKey.private_key !== 'string' || !signingKey.private_key.includes('BEGIN PRIVATE KEY')) failGatewayState('PRIVATE_KEY_INVALID');
|
|
368
|
+
try {
|
|
369
|
+
const probe = { schema: GATEWAY_STATE_SCHEMA, key_id: signingKey.key_id };
|
|
370
|
+
const signature = signPayload(probe, signingKey.private_key);
|
|
371
|
+
if (!verifySignature(probe, signature, signingKey.public_key)) failGatewayState('SIGNING_KEYPAIR_MISMATCH');
|
|
372
|
+
} catch (error) {
|
|
373
|
+
if (error instanceof TypeError && error.message.startsWith('gateway state invalid:')) throw error;
|
|
374
|
+
failGatewayState('SIGNING_KEY_INVALID');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function validateGatewaySiemEvents(events) {
|
|
379
|
+
if (!Array.isArray(events)) failGatewayState('SIEM_EVENTS_ARRAY_REQUIRED');
|
|
380
|
+
for (const event of events) {
|
|
381
|
+
if (!isPlainRecord(event)) failGatewayState('SIEM_EVENT_NOT_OBJECT');
|
|
382
|
+
assertSnapshotKeys(event, new Set([
|
|
383
|
+
'schema',
|
|
384
|
+
'event_id',
|
|
385
|
+
'emitted_at',
|
|
386
|
+
'tenant_id',
|
|
387
|
+
'gateway_id',
|
|
388
|
+
'policy_id',
|
|
389
|
+
'policy_hash',
|
|
390
|
+
'decision_id',
|
|
391
|
+
'decision',
|
|
392
|
+
'allowed',
|
|
393
|
+
'reason_codes',
|
|
394
|
+
'operation_hash',
|
|
395
|
+
'memory_addr_hash',
|
|
396
|
+
'active_root',
|
|
397
|
+
'key_evidence_hash',
|
|
398
|
+
'provider_hash',
|
|
399
|
+
'model_hash',
|
|
400
|
+
'region_hash',
|
|
401
|
+
'purpose_hash',
|
|
402
|
+
'sensitivity_hash',
|
|
403
|
+
]), 'SIEM_EVENT');
|
|
404
|
+
if (eventHasPlaintext(event) || containsPlaintextMemory(event)) failGatewayState('SIEM_EVENT_PLAINTEXT_FORBIDDEN');
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function serializeGatewayState(state) {
|
|
409
|
+
if (!isPlainRecord(state)) failGatewayState('STATE_NOT_OBJECT');
|
|
410
|
+
const policyValidation = validateGatewayPolicy(state.policy);
|
|
411
|
+
if (!policyValidation.ok) failGatewayState(`POLICY_INVALID:${policyValidation.errors.join(',')}`);
|
|
412
|
+
const signingKeyPair = state.signingKeyPair;
|
|
413
|
+
const signingKey = {
|
|
414
|
+
alg: signingKeyPair?.alg ?? 'Ed25519',
|
|
415
|
+
key_id: signingKeyPair?.key_id,
|
|
416
|
+
public_key: signingKeyPair?.publicKey,
|
|
417
|
+
private_key: signingKeyPair?.privateKey,
|
|
418
|
+
};
|
|
419
|
+
validateGatewaySigningKey(signingKey);
|
|
420
|
+
validateGatewaySiemEvents(state.siem_events);
|
|
421
|
+
const snapshot = {
|
|
422
|
+
schema: GATEWAY_STATE_SCHEMA,
|
|
423
|
+
version: GATEWAY_STATE_VERSION,
|
|
424
|
+
gateway_id: state.gateway_id,
|
|
425
|
+
active_root: state.active_root,
|
|
426
|
+
policy: state.policy,
|
|
427
|
+
signing_key: signingKey,
|
|
428
|
+
siem_events: state.siem_events.map((event) => ({ ...event })),
|
|
429
|
+
expose_internal: state.expose_internal === true,
|
|
430
|
+
generated_at: new Date().toISOString(),
|
|
431
|
+
};
|
|
432
|
+
validateGatewayStateSnapshot(snapshot);
|
|
433
|
+
return snapshot;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function hydrateGatewayState(snapshot, options = {}) {
|
|
437
|
+
try {
|
|
438
|
+
const parsed = typeof snapshot === 'string' ? JSON.parse(snapshot) : snapshot;
|
|
439
|
+
validateGatewayStateSnapshot(parsed);
|
|
440
|
+
return createGatewayState({
|
|
441
|
+
gateway_id: parsed.gateway_id,
|
|
442
|
+
active_root: parsed.active_root,
|
|
443
|
+
policy: parsed.policy,
|
|
444
|
+
signingKeyPair: {
|
|
445
|
+
alg: parsed.signing_key.alg,
|
|
446
|
+
key_id: parsed.signing_key.key_id,
|
|
447
|
+
signer: { key_id: parsed.signing_key.key_id, alg: parsed.signing_key.alg },
|
|
448
|
+
publicKey: parsed.signing_key.public_key,
|
|
449
|
+
privateKey: parsed.signing_key.private_key,
|
|
450
|
+
},
|
|
451
|
+
siem_events: parsed.siem_events.map((event) => ({ ...event })),
|
|
452
|
+
expose_internal: parsed.expose_internal === true || options.exposeInternal === true || options.expose_internal === true,
|
|
453
|
+
body_limit: options.body_limit ?? options.bodyLimit,
|
|
454
|
+
});
|
|
455
|
+
} catch (error) {
|
|
456
|
+
if (options.allowDemoReset === true || options.allow_demo_reset === true) {
|
|
457
|
+
return createGatewayState(options.resetOptions ?? {});
|
|
458
|
+
}
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export function loadGatewayStateFromFile(path, options = {}) {
|
|
464
|
+
try {
|
|
465
|
+
return hydrateGatewayState(readFileSync(path, 'utf8'), options);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
if (options.allowDemoReset === true || options.allow_demo_reset === true) {
|
|
468
|
+
return createGatewayState(options.resetOptions ?? {});
|
|
469
|
+
}
|
|
470
|
+
throw error;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export function saveGatewayStateToFile(state, path) {
|
|
475
|
+
const snapshot = serializeGatewayState(state);
|
|
476
|
+
writeFileSync(path, `${JSON.stringify(snapshot, null, 2)}\n`, 'utf8');
|
|
477
|
+
return snapshot;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export function createGatewayState(options = {}) {
|
|
481
|
+
const policy = options.policy ?? defaultPolicy(options.policyOptions ?? options);
|
|
482
|
+
const validation = validateGatewayPolicy(policy);
|
|
483
|
+
if (!validation.ok) {
|
|
484
|
+
throw new TypeError(`gateway policy invalid: ${validation.errors.join(',')}`);
|
|
485
|
+
}
|
|
486
|
+
const state = {
|
|
487
|
+
gateway_id: options.gateway_id ?? options.gatewayId ?? DEFAULT_GATEWAY_ID,
|
|
488
|
+
active_root: options.active_root ?? options.activeRoot ?? DEFAULT_ACTIVE_ROOT,
|
|
489
|
+
policy,
|
|
490
|
+
signingKeyPair: options.signingKeyPair ?? generateSigningKeyPair({ key_id: options.key_id ?? options.keyId ?? 'gateway-local-signing-key' }),
|
|
491
|
+
siem_events: Array.isArray(options.siem_events ?? options.siemEvents) ? [...(options.siem_events ?? options.siemEvents)] : [],
|
|
492
|
+
body_limit: Number.isInteger(options.body_limit ?? options.bodyLimit) ? (options.body_limit ?? options.bodyLimit) : DEFAULT_BODY_LIMIT,
|
|
493
|
+
expose_internal: options.exposeInternal === true || options.expose_internal === true,
|
|
494
|
+
};
|
|
495
|
+
Object.defineProperties(state, {
|
|
496
|
+
mode: {
|
|
497
|
+
value: firstDefined(readinessModeFromOptions(options), 'local'),
|
|
498
|
+
writable: true,
|
|
499
|
+
configurable: true
|
|
500
|
+
},
|
|
501
|
+
production_readiness: {
|
|
502
|
+
value: gatewayProductionReadinessFromOptions(options),
|
|
503
|
+
writable: true,
|
|
504
|
+
configurable: true
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
return state;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function gatewayResponse(status, body, response) {
|
|
511
|
+
const result = { status, headers: JSON_HEADERS, body };
|
|
512
|
+
if (response !== undefined) {
|
|
513
|
+
response.writeHead(status, JSON_HEADERS);
|
|
514
|
+
response.end(JSON.stringify(body));
|
|
515
|
+
}
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async function readJsonBody(request, limit) {
|
|
520
|
+
if (Object.prototype.hasOwnProperty.call(request, 'body')) {
|
|
521
|
+
if (typeof request.body === 'string') return request.body.length === 0 ? {} : JSON.parse(request.body);
|
|
522
|
+
if (Buffer.isBuffer(request.body)) return request.body.byteLength === 0 ? {} : JSON.parse(request.body.toString('utf8'));
|
|
523
|
+
return request.body;
|
|
524
|
+
}
|
|
525
|
+
const chunks = [];
|
|
526
|
+
let total = 0;
|
|
527
|
+
for await (const chunk of request) {
|
|
528
|
+
total += chunk.byteLength;
|
|
529
|
+
if (total > limit) {
|
|
530
|
+
const error = new Error('REQUEST_BODY_TOO_LARGE');
|
|
531
|
+
error.status = 413;
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
chunks.push(chunk);
|
|
535
|
+
}
|
|
536
|
+
if (total === 0) return {};
|
|
537
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function routePath(request) {
|
|
541
|
+
return new URL(request.url ?? '/', 'http://enigma.local').pathname;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function firstDefined(...values) {
|
|
545
|
+
return values.find((value) => value !== undefined);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function nonEmptyString(value) {
|
|
549
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function normalizedBoolean(value) {
|
|
553
|
+
if (typeof value === 'boolean') return value;
|
|
554
|
+
if (!nonEmptyString(value)) return undefined;
|
|
555
|
+
switch (value.trim().toLowerCase()) {
|
|
556
|
+
case '1':
|
|
557
|
+
case 'true':
|
|
558
|
+
case 'yes':
|
|
559
|
+
case 'y':
|
|
560
|
+
case 'on':
|
|
561
|
+
case 'enabled':
|
|
562
|
+
case 'required':
|
|
563
|
+
case 'require':
|
|
564
|
+
return true;
|
|
565
|
+
case '0':
|
|
566
|
+
case 'false':
|
|
567
|
+
case 'no':
|
|
568
|
+
case 'n':
|
|
569
|
+
case 'off':
|
|
570
|
+
case 'disabled':
|
|
571
|
+
return false;
|
|
572
|
+
default:
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function envValue(...keys) {
|
|
578
|
+
const env = globalThis.process?.env;
|
|
579
|
+
if (!isPlainRecord(env)) return undefined;
|
|
580
|
+
for (const key of keys) {
|
|
581
|
+
const value = env[key];
|
|
582
|
+
if (nonEmptyString(value)) return value;
|
|
583
|
+
}
|
|
584
|
+
return undefined;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function envBoolean(...keys) {
|
|
588
|
+
const env = globalThis.process?.env;
|
|
589
|
+
if (!isPlainRecord(env)) return undefined;
|
|
590
|
+
for (const key of keys) {
|
|
591
|
+
const value = normalizedBoolean(env[key]);
|
|
592
|
+
if (value !== undefined) return value;
|
|
593
|
+
}
|
|
594
|
+
return undefined;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function envProductionModeFallback() {
|
|
598
|
+
const nodeEnv = envValue('NODE_ENV');
|
|
599
|
+
return nonEmptyString(nodeEnv) && nodeEnv.trim().toLowerCase() === 'production' ? 'production' : undefined;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function readinessFailClosedFromOptions(options) {
|
|
603
|
+
return normalizedBoolean(firstDefined(
|
|
604
|
+
options.readinessFailClosed,
|
|
605
|
+
options.readiness_fail_closed,
|
|
606
|
+
options.failClosed,
|
|
607
|
+
options.fail_closed,
|
|
608
|
+
envBoolean('ENIGMA_READINESS_FAIL_CLOSED', 'ENIGMA_DISABLE_LOCAL_DEMO_FALLBACK')
|
|
609
|
+
));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function readinessModeFromOptions(options) {
|
|
613
|
+
return firstDefined(
|
|
614
|
+
options.mode,
|
|
615
|
+
options.environment,
|
|
616
|
+
options.readinessMode,
|
|
617
|
+
options.readiness_mode,
|
|
618
|
+
envValue('ENIGMA_BACKEND_MODE'),
|
|
619
|
+
readinessFailClosedFromOptions(options) === true ? 'production' : undefined,
|
|
620
|
+
envProductionModeFallback()
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function productionLikeFromOptions(options) {
|
|
625
|
+
return options.production === true
|
|
626
|
+
|| options.productionLike === true
|
|
627
|
+
|| options.production_like === true
|
|
628
|
+
|| options.hosted === true
|
|
629
|
+
|| options.byoc === true
|
|
630
|
+
|| readinessFailClosedFromOptions(options) === true;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function optionValue(record, ...keys) {
|
|
634
|
+
if (!isPlainRecord(record)) return undefined;
|
|
635
|
+
for (const key of keys) {
|
|
636
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) return record[key];
|
|
637
|
+
}
|
|
638
|
+
return undefined;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function readinessRecord(state) {
|
|
642
|
+
const readiness = optionValue(state, 'production_readiness', 'productionReadiness', 'readiness');
|
|
643
|
+
return isPlainRecord(readiness) ? readiness : {};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function readinessValue(state, ...keys) {
|
|
647
|
+
const direct = optionValue(state, ...keys);
|
|
648
|
+
if (direct !== undefined) return direct;
|
|
649
|
+
return optionValue(readinessRecord(state), ...keys);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function sharedProductionDependencyRefsFromOptions(options) {
|
|
653
|
+
const refs = {};
|
|
654
|
+
for (const key of SHARED_PRODUCTION_DEPENDENCY_KEYS) {
|
|
655
|
+
const aliases = SHARED_PRODUCTION_DEPENDENCY_ALIASES[key];
|
|
656
|
+
refs[key] = firstDefined(
|
|
657
|
+
...aliases.options.map((name) => options[name]),
|
|
658
|
+
envValue(...aliases.env)
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
return refs;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function productionDependencyValue(state, key) {
|
|
665
|
+
const aliases = SHARED_PRODUCTION_DEPENDENCY_ALIASES[key];
|
|
666
|
+
return readinessValue(state, key, ...aliases.options);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function productionDependencyConfigured(state, key) {
|
|
670
|
+
return hasConfiguredReference(productionDependencyValue(state, key));
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function normalizedReadinessMode(state) {
|
|
674
|
+
const mode = readinessValue(state, 'mode', 'environment', 'readinessMode', 'readiness_mode');
|
|
675
|
+
return nonEmptyString(mode) ? mode.trim().toLowerCase() : 'local';
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function isProductionLikeState(state) {
|
|
679
|
+
const mode = normalizedReadinessMode(state);
|
|
680
|
+
return PRODUCTION_READINESS_MODES.has(mode)
|
|
681
|
+
|| readinessValue(state, 'production', 'production_like', 'productionLike', 'hosted', 'byoc') === true;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function isLocalReadinessMode(state) {
|
|
685
|
+
return LOCAL_READINESS_MODES.has(normalizedReadinessMode(state));
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function readinessKind(value) {
|
|
689
|
+
if (nonEmptyString(value)) return value.trim().toLowerCase();
|
|
690
|
+
if (!isPlainRecord(value)) return undefined;
|
|
691
|
+
const kind = firstDefined(value.kind, value.type, value.backend, value.driver, value.provider);
|
|
692
|
+
return nonEmptyString(kind) ? kind.trim().toLowerCase() : undefined;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function hasConfiguredReference(value) {
|
|
696
|
+
if (value === true) return true;
|
|
697
|
+
if (nonEmptyString(value)) return true;
|
|
698
|
+
if (!isPlainRecord(value)) return false;
|
|
699
|
+
if (value.configured === true || value.enabled === true || value.ready === true) return true;
|
|
700
|
+
return ['ref', 'reference', 'id', 'name', 'uri', 'url', 'arn', 'path', 'target', 'sink', 'key_ref', 'keyRef', 'resource'].some((key) => nonEmptyString(value[key]));
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function gatewayAuthHash(state, kind) {
|
|
704
|
+
if (kind === 'admin') {
|
|
705
|
+
return normalizeSha256Digest(readinessValue(state, 'admin_auth_bearer_sha256', 'adminAuthBearerSha256', 'admin_bearer_sha256', 'adminBearerSha256'));
|
|
706
|
+
}
|
|
707
|
+
return normalizeSha256Digest(readinessValue(state, 'data_plane_auth_bearer_sha256', 'dataPlaneAuthBearerSha256', 'data_plane_bearer_sha256', 'dataPlaneBearerSha256'));
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function authorizeGatewayRoute(state, request, kind) {
|
|
711
|
+
if (!isProductionLikeState(state)) return { ok: true };
|
|
712
|
+
const expected = gatewayAuthHash(state, kind);
|
|
713
|
+
if (!expected) {
|
|
714
|
+
return {
|
|
715
|
+
ok: false,
|
|
716
|
+
status: 503,
|
|
717
|
+
code: 'GATEWAY_AUTH_NOT_CONFIGURED',
|
|
718
|
+
reason_codes: [`${kind.toUpperCase()}_AUTH_NOT_CONFIGURED`],
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
const token = bearerTokenFromRequest(request);
|
|
722
|
+
if (!nonEmptyString(token)) {
|
|
723
|
+
return {
|
|
724
|
+
ok: false,
|
|
725
|
+
status: 401,
|
|
726
|
+
code: 'GATEWAY_AUTH_REQUIRED',
|
|
727
|
+
reason_codes: [`${kind.toUpperCase()}_AUTH_REQUIRED`],
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
if (bearerTokenHash(token) !== expected) {
|
|
731
|
+
return {
|
|
732
|
+
ok: false,
|
|
733
|
+
status: 403,
|
|
734
|
+
code: 'GATEWAY_AUTH_DENIED',
|
|
735
|
+
reason_codes: [`${kind.toUpperCase()}_AUTH_DENIED`],
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
return { ok: true };
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function gatewayAuthResponse(auth, response) {
|
|
742
|
+
return gatewayResponse(auth.status, {
|
|
743
|
+
ok: false,
|
|
744
|
+
error: {
|
|
745
|
+
code: auth.code,
|
|
746
|
+
reason_codes: auth.reason_codes,
|
|
747
|
+
},
|
|
748
|
+
}, response);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function hasDurableStorage(value) {
|
|
752
|
+
if (value === true) return true;
|
|
753
|
+
const kind = readinessKind(value);
|
|
754
|
+
if (kind !== undefined) {
|
|
755
|
+
if (LOCAL_STORAGE_KINDS.has(kind)) return false;
|
|
756
|
+
if (DURABLE_STORAGE_KINDS.has(kind)) return true;
|
|
757
|
+
}
|
|
758
|
+
if (nonEmptyString(value)) return !LOCAL_STORAGE_KINDS.has(value.trim().toLowerCase());
|
|
759
|
+
if (!isPlainRecord(value)) return false;
|
|
760
|
+
if (value.durable === true || value.production === true) return true;
|
|
761
|
+
if (value.in_memory === true || value.inMemory === true || value.local === true || value.demo === true) return false;
|
|
762
|
+
return hasConfiguredReference(value);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function operatorAcceptanceIsGo(value) {
|
|
766
|
+
if (nonEmptyString(value)) return value.trim().toLowerCase() === 'go';
|
|
767
|
+
if (!isPlainRecord(value)) return false;
|
|
768
|
+
return operatorAcceptanceIsGo(firstDefined(value.status, value.decision, value.state, value.operator_acceptance, value.operatorAcceptance));
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function unauthenticatedLocalDemoEnabled(state) {
|
|
772
|
+
return state.allowUnauthenticated === true
|
|
773
|
+
|| state.allow_unauthenticated === true
|
|
774
|
+
|| readinessValue(state, 'unauthenticated_local_demo', 'unauthenticatedLocalDemo') === true;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function gatewayProductionReadinessFromOptions(options) {
|
|
778
|
+
return {
|
|
779
|
+
mode: readinessModeFromOptions(options),
|
|
780
|
+
production_like: productionLikeFromOptions(options),
|
|
781
|
+
backend_host: firstDefined(
|
|
782
|
+
options.backend_host,
|
|
783
|
+
options.backendHost,
|
|
784
|
+
options.backend_host_ref,
|
|
785
|
+
options.backendHostRef,
|
|
786
|
+
options.host_ref,
|
|
787
|
+
options.hostRef,
|
|
788
|
+
options.deployment_ref,
|
|
789
|
+
options.deploymentRef,
|
|
790
|
+
envValue('ENIGMA_BACKEND_HOST_REF', 'ENIGMA_GATEWAY_BACKEND_HOST_REF', 'ENIGMA_GATEWAY_DEPLOYMENT_REF')
|
|
791
|
+
),
|
|
792
|
+
dns_tls: firstDefined(
|
|
793
|
+
options.dns_tls,
|
|
794
|
+
options.dnsTls,
|
|
795
|
+
options.dns_tls_ref,
|
|
796
|
+
options.dnsTlsRef,
|
|
797
|
+
options.tls_ref,
|
|
798
|
+
options.tlsRef,
|
|
799
|
+
envValue('ENIGMA_DNS_TLS_REF', 'ENIGMA_GATEWAY_DNS_TLS_REF', 'ENIGMA_TLS_REF')
|
|
800
|
+
),
|
|
801
|
+
durable_storage: firstDefined(
|
|
802
|
+
options.durable_storage,
|
|
803
|
+
options.durableStorage,
|
|
804
|
+
options.storage,
|
|
805
|
+
options.storage_ref,
|
|
806
|
+
options.storageRef,
|
|
807
|
+
options.state_backend,
|
|
808
|
+
options.stateBackend,
|
|
809
|
+
options.persistence,
|
|
810
|
+
options.persistence_backend,
|
|
811
|
+
options.persistenceBackend,
|
|
812
|
+
envValue('ENIGMA_EXTERNAL_STORAGE_DSN', 'ENIGMA_EXTERNAL_STORAGE_DSN_FILE', 'ENIGMA_EXTERNAL_STORAGE_REF', 'ENIGMA_GATEWAY_STORAGE_REF', 'ENIGMA_DURABLE_STORAGE_REF', 'ENIGMA_GATEWAY_STATE_BACKEND')
|
|
813
|
+
),
|
|
814
|
+
kms_or_secret_custody: firstDefined(
|
|
815
|
+
options.kms_or_secret_custody,
|
|
816
|
+
options.kmsOrSecretCustody,
|
|
817
|
+
options.kms,
|
|
818
|
+
options.kms_ref,
|
|
819
|
+
options.kmsRef,
|
|
820
|
+
options.signer_ref,
|
|
821
|
+
options.signerRef,
|
|
822
|
+
options.signing_key_ref,
|
|
823
|
+
options.signingKeyRef,
|
|
824
|
+
options.kmsKeyRef,
|
|
825
|
+
options.kms_key_ref,
|
|
826
|
+
envValue('ENIGMA_KMS_KEY_REF', 'ENIGMA_KMS_REF', 'ENIGMA_EXTERNAL_KMS_REF', 'ENIGMA_GATEWAY_SIGNER_REF')
|
|
827
|
+
),
|
|
828
|
+
siem_or_log_sink: firstDefined(
|
|
829
|
+
options.siem_or_log_sink,
|
|
830
|
+
options.siemOrLogSink,
|
|
831
|
+
options.siem,
|
|
832
|
+
options.siem_ref,
|
|
833
|
+
options.siemRef,
|
|
834
|
+
options.audit_sink,
|
|
835
|
+
options.auditSink,
|
|
836
|
+
options.log_sink,
|
|
837
|
+
options.logSink,
|
|
838
|
+
envValue('ENIGMA_SIEM_EXPORT_ENDPOINT', 'ENIGMA_SIEM_EXPORT_ENDPOINT_FILE', 'ENIGMA_SIEM_REF', 'ENIGMA_AUDIT_SINK_REF', 'ENIGMA_LOG_SINK_REF')
|
|
839
|
+
),
|
|
840
|
+
backup_restore: firstDefined(
|
|
841
|
+
options.backup_restore,
|
|
842
|
+
options.backupRestore,
|
|
843
|
+
options.backup,
|
|
844
|
+
options.backup_target,
|
|
845
|
+
options.backupTarget,
|
|
846
|
+
options.restore_target,
|
|
847
|
+
options.restoreTarget,
|
|
848
|
+
envValue('ENIGMA_BACKUP_TARGET_URI', 'ENIGMA_BACKUP_TARGET_URI_FILE', 'ENIGMA_BACKUP_TARGET_REF', 'ENIGMA_RESTORE_TARGET_REF')
|
|
849
|
+
),
|
|
850
|
+
monitoring: firstDefined(
|
|
851
|
+
options.monitoring,
|
|
852
|
+
options.monitoring_ref,
|
|
853
|
+
options.monitoringRef,
|
|
854
|
+
options.alerting,
|
|
855
|
+
options.alerting_ref,
|
|
856
|
+
options.alertingRef,
|
|
857
|
+
envValue('ENIGMA_MONITORING_REF', 'ENIGMA_GATEWAY_MONITORING_REF', 'ENIGMA_ALERTING_REF')
|
|
858
|
+
),
|
|
859
|
+
admin_auth: firstDefined(
|
|
860
|
+
options.admin_auth,
|
|
861
|
+
options.adminAuth,
|
|
862
|
+
options.admin_auth_ref,
|
|
863
|
+
options.adminAuthRef,
|
|
864
|
+
envValue('ENIGMA_ADMIN_AUTH_REF', 'ENIGMA_GATEWAY_ADMIN_AUTH_REF')
|
|
865
|
+
),
|
|
866
|
+
data_plane_auth: firstDefined(
|
|
867
|
+
options.data_plane_auth,
|
|
868
|
+
options.dataPlaneAuth,
|
|
869
|
+
options.data_plane_auth_ref,
|
|
870
|
+
options.dataPlaneAuthRef,
|
|
871
|
+
envValue('ENIGMA_DATA_PLANE_AUTH_REF', 'ENIGMA_GATEWAY_DATA_PLANE_AUTH_REF')
|
|
872
|
+
),
|
|
873
|
+
...sharedProductionDependencyRefsFromOptions(options),
|
|
874
|
+
durable_storage_configured: hasDurableStorage(firstDefined(
|
|
875
|
+
options.durable_storage_configured,
|
|
876
|
+
options.durableStorageConfigured,
|
|
877
|
+
options.durable_storage,
|
|
878
|
+
options.durableStorage,
|
|
879
|
+
options.storage,
|
|
880
|
+
options.storage_ref,
|
|
881
|
+
options.storageRef,
|
|
882
|
+
options.state_backend,
|
|
883
|
+
options.stateBackend,
|
|
884
|
+
options.persistence,
|
|
885
|
+
options.persistence_backend,
|
|
886
|
+
options.persistenceBackend,
|
|
887
|
+
envValue(
|
|
888
|
+
'ENIGMA_EXTERNAL_STORAGE_DSN',
|
|
889
|
+
'ENIGMA_EXTERNAL_STORAGE_DSN_FILE',
|
|
890
|
+
'ENIGMA_EXTERNAL_STORAGE_REF',
|
|
891
|
+
'ENIGMA_EXTERNAL_STORAGE_REF_FILE',
|
|
892
|
+
'ENIGMA_STORAGE_REF',
|
|
893
|
+
'ENIGMA_STORAGE_REF_FILE',
|
|
894
|
+
'ENIGMA_GATEWAY_STORAGE_REF',
|
|
895
|
+
'ENIGMA_GATEWAY_STORAGE_REF_FILE',
|
|
896
|
+
'ENIGMA_DURABLE_STORAGE_REF',
|
|
897
|
+
'ENIGMA_DURABLE_STORAGE_REF_FILE',
|
|
898
|
+
'ENIGMA_STATE_BACKEND',
|
|
899
|
+
'ENIGMA_GATEWAY_STATE_BACKEND',
|
|
900
|
+
'ENIGMA_PERSISTENCE_BACKEND',
|
|
901
|
+
'ENIGMA_GATEWAY_PERSISTENCE_BACKEND'
|
|
902
|
+
)
|
|
903
|
+
)),
|
|
904
|
+
kms_or_signer_ref_configured: hasConfiguredReference(firstDefined(
|
|
905
|
+
options.kms_or_signer_ref_configured,
|
|
906
|
+
options.kmsOrSignerRefConfigured,
|
|
907
|
+
options.kms,
|
|
908
|
+
options.kms_ref,
|
|
909
|
+
options.kmsRef,
|
|
910
|
+
options.signer_ref,
|
|
911
|
+
options.signerRef,
|
|
912
|
+
options.signing_key_ref,
|
|
913
|
+
options.signingKeyRef,
|
|
914
|
+
options.kmsKeyRef,
|
|
915
|
+
options.kms_key_ref,
|
|
916
|
+
envValue(
|
|
917
|
+
'ENIGMA_KMS_KEY_REF',
|
|
918
|
+
'ENIGMA_KMS_KEY_REF_FILE',
|
|
919
|
+
'ENIGMA_KMS_REF',
|
|
920
|
+
'ENIGMA_KMS_REF_FILE',
|
|
921
|
+
'ENIGMA_EXTERNAL_KMS_REF',
|
|
922
|
+
'ENIGMA_EXTERNAL_KMS_REF_FILE',
|
|
923
|
+
'ENIGMA_GATEWAY_SIGNING_KEY_REF',
|
|
924
|
+
'ENIGMA_GATEWAY_SIGNING_KEY_FILE',
|
|
925
|
+
'ENIGMA_SIGNING_KEY_REF',
|
|
926
|
+
'ENIGMA_SIGNING_KEY_FILE',
|
|
927
|
+
'ENIGMA_SIGNER_REF',
|
|
928
|
+
'ENIGMA_SIGNER_REF_FILE',
|
|
929
|
+
'ENIGMA_GATEWAY_SIGNER_REF',
|
|
930
|
+
'ENIGMA_GATEWAY_SIGNER_REF_FILE'
|
|
931
|
+
)
|
|
932
|
+
)),
|
|
933
|
+
siem_or_audit_configured: hasConfiguredReference(firstDefined(
|
|
934
|
+
options.siem_or_audit_configured,
|
|
935
|
+
options.siemOrAuditConfigured,
|
|
936
|
+
options.siem,
|
|
937
|
+
options.siem_ref,
|
|
938
|
+
options.siemRef,
|
|
939
|
+
options.audit,
|
|
940
|
+
options.audit_sink,
|
|
941
|
+
options.auditSink,
|
|
942
|
+
options.log_sink,
|
|
943
|
+
options.logSink,
|
|
944
|
+
envValue(
|
|
945
|
+
'ENIGMA_SIEM_EXPORT_ENDPOINT',
|
|
946
|
+
'ENIGMA_SIEM_EXPORT_ENDPOINT_FILE',
|
|
947
|
+
'ENIGMA_SIEM_REF',
|
|
948
|
+
'ENIGMA_SIEM_REF_FILE',
|
|
949
|
+
'ENIGMA_AUDIT_SINK_REF',
|
|
950
|
+
'ENIGMA_AUDIT_SINK_REF_FILE',
|
|
951
|
+
'ENIGMA_LOG_SINK_REF',
|
|
952
|
+
'ENIGMA_LOG_SINK_REF_FILE'
|
|
953
|
+
)
|
|
954
|
+
)),
|
|
955
|
+
backup_configured: hasConfiguredReference(firstDefined(
|
|
956
|
+
options.backup_configured,
|
|
957
|
+
options.backupConfigured,
|
|
958
|
+
options.backup,
|
|
959
|
+
options.backup_target,
|
|
960
|
+
options.backupTarget,
|
|
961
|
+
options.restore_target,
|
|
962
|
+
options.restoreTarget,
|
|
963
|
+
envValue(
|
|
964
|
+
'ENIGMA_BACKUP_TARGET_URI',
|
|
965
|
+
'ENIGMA_BACKUP_TARGET_URI_FILE',
|
|
966
|
+
'ENIGMA_BACKUP_TARGET',
|
|
967
|
+
'ENIGMA_BACKUP_TARGET_FILE',
|
|
968
|
+
'ENIGMA_BACKUP_TARGET_REF',
|
|
969
|
+
'ENIGMA_BACKUP_TARGET_REF_FILE',
|
|
970
|
+
'ENIGMA_RESTORE_TARGET_REF',
|
|
971
|
+
'ENIGMA_RESTORE_TARGET_REF_FILE'
|
|
972
|
+
)
|
|
973
|
+
)),
|
|
974
|
+
admin_auth_configured: hasConfiguredReference(firstDefined(
|
|
975
|
+
options.admin_auth_configured,
|
|
976
|
+
options.adminAuthConfigured,
|
|
977
|
+
options.admin_auth,
|
|
978
|
+
options.adminAuth,
|
|
979
|
+
options.admin_auth_ref,
|
|
980
|
+
options.adminAuthRef,
|
|
981
|
+
envValue(
|
|
982
|
+
'ENIGMA_ADMIN_AUTH_REF',
|
|
983
|
+
'ENIGMA_ADMIN_AUTH_REF_FILE',
|
|
984
|
+
'ENIGMA_ADMIN_AUTH',
|
|
985
|
+
'ENIGMA_ADMIN_AUTH_URI',
|
|
986
|
+
'ENIGMA_GATEWAY_ADMIN_AUTH_REF',
|
|
987
|
+
'ENIGMA_GATEWAY_ADMIN_AUTH_REF_FILE',
|
|
988
|
+
'ENIGMA_GATEWAY_ADMIN_AUTH_URI'
|
|
989
|
+
)
|
|
990
|
+
)),
|
|
991
|
+
admin_auth_bearer_sha256: normalizeSha256Digest(firstDefined(
|
|
992
|
+
options.admin_auth_bearer_sha256,
|
|
993
|
+
options.adminAuthBearerSha256,
|
|
994
|
+
options.admin_bearer_sha256,
|
|
995
|
+
options.adminBearerSha256,
|
|
996
|
+
envValue('ENIGMA_GATEWAY_ADMIN_AUTH_BEARER_SHA256', 'ENIGMA_ADMIN_AUTH_BEARER_SHA256')
|
|
997
|
+
)),
|
|
998
|
+
data_plane_auth_configured: hasConfiguredReference(firstDefined(
|
|
999
|
+
options.data_plane_auth_configured,
|
|
1000
|
+
options.dataPlaneAuthConfigured,
|
|
1001
|
+
options.data_plane_auth,
|
|
1002
|
+
options.dataPlaneAuth,
|
|
1003
|
+
options.data_plane_auth_ref,
|
|
1004
|
+
options.dataPlaneAuthRef,
|
|
1005
|
+
envValue(
|
|
1006
|
+
'ENIGMA_DATA_PLANE_AUTH_REF',
|
|
1007
|
+
'ENIGMA_DATA_PLANE_AUTH_REF_FILE',
|
|
1008
|
+
'ENIGMA_DATA_PLANE_AUTH',
|
|
1009
|
+
'ENIGMA_DATA_PLANE_AUTH_URI',
|
|
1010
|
+
'ENIGMA_GATEWAY_DATA_PLANE_AUTH_REF',
|
|
1011
|
+
'ENIGMA_GATEWAY_DATA_PLANE_AUTH_REF_FILE',
|
|
1012
|
+
'ENIGMA_GATEWAY_DATA_PLANE_AUTH_URI'
|
|
1013
|
+
)
|
|
1014
|
+
)),
|
|
1015
|
+
data_plane_auth_bearer_sha256: normalizeSha256Digest(firstDefined(
|
|
1016
|
+
options.data_plane_auth_bearer_sha256,
|
|
1017
|
+
options.dataPlaneAuthBearerSha256,
|
|
1018
|
+
options.data_plane_bearer_sha256,
|
|
1019
|
+
options.dataPlaneBearerSha256,
|
|
1020
|
+
envValue('ENIGMA_GATEWAY_DATA_PLANE_AUTH_BEARER_SHA256', 'ENIGMA_DATA_PLANE_AUTH_BEARER_SHA256')
|
|
1021
|
+
)),
|
|
1022
|
+
operator_acceptance: firstDefined(
|
|
1023
|
+
options.operator_acceptance,
|
|
1024
|
+
options.operatorAcceptance,
|
|
1025
|
+
options.acceptance,
|
|
1026
|
+
options.go_live,
|
|
1027
|
+
options.goLive,
|
|
1028
|
+
envValue(
|
|
1029
|
+
'ENIGMA_OPERATOR_ACCEPTANCE',
|
|
1030
|
+
'ENIGMA_OPERATOR_ACCEPTANCE_DECISION',
|
|
1031
|
+
'ENIGMA_GO_LIVE_DECISION',
|
|
1032
|
+
'ENIGMA_GO_LIVE'
|
|
1033
|
+
)
|
|
1034
|
+
),
|
|
1035
|
+
state_backend: firstDefined(
|
|
1036
|
+
options.state_backend,
|
|
1037
|
+
options.stateBackend,
|
|
1038
|
+
options.persistence,
|
|
1039
|
+
options.persistence_backend,
|
|
1040
|
+
options.persistenceBackend,
|
|
1041
|
+
envValue(
|
|
1042
|
+
'ENIGMA_STATE_BACKEND',
|
|
1043
|
+
'ENIGMA_GATEWAY_STATE_BACKEND',
|
|
1044
|
+
'ENIGMA_PERSISTENCE_BACKEND',
|
|
1045
|
+
'ENIGMA_GATEWAY_PERSISTENCE_BACKEND',
|
|
1046
|
+
'ENIGMA_EXTERNAL_STORAGE_DSN',
|
|
1047
|
+
'ENIGMA_EXTERNAL_STORAGE_DSN_FILE'
|
|
1048
|
+
)
|
|
1049
|
+
)
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
function gatewayReadinessCheck(check, okValue, missing) {
|
|
1054
|
+
const ok = okValue === true;
|
|
1055
|
+
return ok ? { check, ok: true } : { check, ok: false, missing };
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function productionEvidenceRef(key, value, status = 'verified') {
|
|
1059
|
+
if (value === undefined || value === null || value === false || value === true) return undefined;
|
|
1060
|
+
if (nonEmptyString(value)) return { status, provider: 'operator', ref: value };
|
|
1061
|
+
if (!isPlainRecord(value)) return undefined;
|
|
1062
|
+
const ref = firstDefined(
|
|
1063
|
+
value.ref,
|
|
1064
|
+
value.reference,
|
|
1065
|
+
value.id,
|
|
1066
|
+
value.name,
|
|
1067
|
+
value.uri,
|
|
1068
|
+
value.url,
|
|
1069
|
+
value.arn,
|
|
1070
|
+
value.path,
|
|
1071
|
+
value.target,
|
|
1072
|
+
value.sink,
|
|
1073
|
+
value.key_ref,
|
|
1074
|
+
value.keyRef,
|
|
1075
|
+
value.resource
|
|
1076
|
+
);
|
|
1077
|
+
if (!nonEmptyString(ref)) return undefined;
|
|
1078
|
+
return {
|
|
1079
|
+
...value,
|
|
1080
|
+
key,
|
|
1081
|
+
ref,
|
|
1082
|
+
status: value.status ?? status,
|
|
1083
|
+
provider: value.provider ?? 'operator',
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function gatewayProductionEvidence(state) {
|
|
1088
|
+
const evidence = {};
|
|
1089
|
+
const add = (key, value, status = 'verified') => {
|
|
1090
|
+
const ref = productionEvidenceRef(key, value, status);
|
|
1091
|
+
if (ref !== undefined) evidence[key] = ref;
|
|
1092
|
+
};
|
|
1093
|
+
add('backend_host', readinessValue(state, 'backend_host', 'backendHost', 'backend_host_ref', 'backendHostRef', 'host_ref', 'hostRef', 'deployment_ref', 'deploymentRef'));
|
|
1094
|
+
add('dns_tls', readinessValue(state, 'dns_tls', 'dnsTls', 'dns_tls_ref', 'dnsTlsRef', 'tls_ref', 'tlsRef'));
|
|
1095
|
+
add('durable_storage', readinessValue(state, 'durable_storage', 'durableStorage', 'storage', 'storage_ref', 'storageRef', 'state_backend', 'stateBackend', 'persistence', 'persistence_backend', 'persistenceBackend'));
|
|
1096
|
+
add('kms_or_secret_custody', readinessValue(state, 'kms_or_secret_custody', 'kmsOrSecretCustody', 'kms', 'kms_ref', 'kmsRef', 'signer_ref', 'signerRef', 'signing_key_ref', 'signingKeyRef', 'kms_key_ref', 'kmsKeyRef'));
|
|
1097
|
+
add('backup_restore', readinessValue(state, 'backup_restore', 'backupRestore', 'backup', 'backup_target', 'backupTarget', 'restore_target', 'restoreTarget'));
|
|
1098
|
+
add('monitoring', readinessValue(state, 'monitoring', 'monitoring_ref', 'monitoringRef', 'alerting', 'alerting_ref', 'alertingRef'));
|
|
1099
|
+
add('siem_or_log_sink', readinessValue(state, 'siem_or_log_sink', 'siemOrLogSink', 'siem', 'siem_ref', 'siemRef', 'audit_sink', 'auditSink', 'log_sink', 'logSink'));
|
|
1100
|
+
add('operator_acceptance', readinessValue(state, 'operator_acceptance', 'operatorAcceptance', 'acceptance', 'go_live', 'goLive'), 'go');
|
|
1101
|
+
add('admin_auth', readinessValue(state, 'admin_auth', 'adminAuth', 'admin_auth_ref', 'adminAuthRef'));
|
|
1102
|
+
add('data_plane_auth', readinessValue(state, 'data_plane_auth', 'dataPlaneAuth', 'data_plane_auth_ref', 'dataPlaneAuthRef'));
|
|
1103
|
+
for (const key of SHARED_PRODUCTION_DEPENDENCY_KEYS) add(key, productionDependencyValue(state, key));
|
|
1104
|
+
try {
|
|
1105
|
+
const normalized = normalizeDependencyEvidence(evidence, {
|
|
1106
|
+
generated_at: new Date(0).toISOString(),
|
|
1107
|
+
required_keys: [
|
|
1108
|
+
'backend_host',
|
|
1109
|
+
'dns_tls',
|
|
1110
|
+
'durable_storage',
|
|
1111
|
+
'kms_or_secret_custody',
|
|
1112
|
+
'backup_restore',
|
|
1113
|
+
'monitoring',
|
|
1114
|
+
'siem_or_log_sink',
|
|
1115
|
+
'operator_acceptance',
|
|
1116
|
+
'admin_auth',
|
|
1117
|
+
'data_plane_auth',
|
|
1118
|
+
...SHARED_PRODUCTION_DEPENDENCY_KEYS,
|
|
1119
|
+
],
|
|
1120
|
+
});
|
|
1121
|
+
return {
|
|
1122
|
+
ok: normalized.ok,
|
|
1123
|
+
refs: readinessEvidenceRefs(normalized.evidence),
|
|
1124
|
+
missing_keys: normalized.missing_keys,
|
|
1125
|
+
};
|
|
1126
|
+
} catch {
|
|
1127
|
+
return {
|
|
1128
|
+
ok: false,
|
|
1129
|
+
refs: {},
|
|
1130
|
+
missing_keys: ['production_dependency_evidence'],
|
|
1131
|
+
error: 'production dependency evidence refs are invalid or unsafe',
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function gatewayStateIsLocalOnly(state, durableStorage) {
|
|
1137
|
+
const backend = firstDefined(
|
|
1138
|
+
readinessValue(state, 'state_backend', 'stateBackend', 'persistence', 'persistence_backend', 'persistenceBackend'),
|
|
1139
|
+
readinessValue(state, 'storage', 'durable_storage', 'durableStorage')
|
|
1140
|
+
);
|
|
1141
|
+
const kind = readinessKind(backend);
|
|
1142
|
+
if (kind !== undefined && LOCAL_STORAGE_KINDS.has(kind)) return true;
|
|
1143
|
+
return durableStorage !== true;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function gatewayKmsOrSignerRefConfigured(state) {
|
|
1147
|
+
const signingKeyPair = isPlainRecord(state.signingKeyPair) ? state.signingKeyPair : {};
|
|
1148
|
+
return hasConfiguredReference(firstDefined(
|
|
1149
|
+
readinessValue(state, 'kms', 'kms_ref', 'kmsRef', 'kms_key_ref', 'kmsKeyRef', 'signer_ref', 'signerRef', 'signing_key_ref', 'signingKeyRef'),
|
|
1150
|
+
readinessValue(state, 'kms_or_signer_ref_configured'),
|
|
1151
|
+
optionValue(signingKeyPair, 'kms_ref', 'kmsRef', 'kms_key_ref', 'kmsKeyRef', 'signer_ref', 'signerRef', 'signing_key_ref', 'signingKeyRef')
|
|
1152
|
+
));
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function gatewayReadiness(state) {
|
|
1156
|
+
const productionLike = isProductionLikeState(state);
|
|
1157
|
+
const evidence = productionLike ? gatewayProductionEvidence(state) : { ok: true, refs: {}, missing_keys: [] };
|
|
1158
|
+
const backendHost = hasConfiguredReference(readinessValue(state, 'backend_host', 'backendHost', 'backend_host_ref', 'backendHostRef', 'host_ref', 'hostRef', 'deployment_ref', 'deploymentRef'));
|
|
1159
|
+
const dnsTls = hasConfiguredReference(readinessValue(state, 'dns_tls', 'dnsTls', 'dns_tls_ref', 'dnsTlsRef', 'tls_ref', 'tlsRef'));
|
|
1160
|
+
const monitoring = hasConfiguredReference(readinessValue(state, 'monitoring', 'monitoring_ref', 'monitoringRef', 'alerting', 'alerting_ref', 'alertingRef'));
|
|
1161
|
+
const durableStorage = hasDurableStorage(firstDefined(
|
|
1162
|
+
readinessValue(state, 'durable_storage', 'durableStorage', 'storage', 'storage_ref', 'storageRef'),
|
|
1163
|
+
readinessValue(state, 'durable_storage_configured')
|
|
1164
|
+
));
|
|
1165
|
+
const kmsOrSigner = gatewayKmsOrSignerRefConfigured(state);
|
|
1166
|
+
const siemOrAudit = hasConfiguredReference(firstDefined(
|
|
1167
|
+
readinessValue(state, 'siem', 'siem_ref', 'siemRef', 'audit', 'audit_sink', 'auditSink', 'log_sink', 'logSink'),
|
|
1168
|
+
readinessValue(state, 'siem_or_audit_configured')
|
|
1169
|
+
));
|
|
1170
|
+
const backup = hasConfiguredReference(firstDefined(
|
|
1171
|
+
readinessValue(state, 'backup', 'backup_target', 'backupTarget', 'restore_target', 'restoreTarget'),
|
|
1172
|
+
readinessValue(state, 'backup_configured')
|
|
1173
|
+
));
|
|
1174
|
+
const adminAuth = hasConfiguredReference(firstDefined(
|
|
1175
|
+
readinessValue(state, 'admin_auth', 'adminAuth', 'admin_auth_ref', 'adminAuthRef'),
|
|
1176
|
+
readinessValue(state, 'admin_auth_configured')
|
|
1177
|
+
));
|
|
1178
|
+
const dataPlaneAuth = hasConfiguredReference(firstDefined(
|
|
1179
|
+
readinessValue(state, 'data_plane_auth', 'dataPlaneAuth', 'data_plane_auth_ref', 'dataPlaneAuthRef'),
|
|
1180
|
+
readinessValue(state, 'data_plane_auth_configured')
|
|
1181
|
+
));
|
|
1182
|
+
const operatorAccepted = operatorAcceptanceIsGo(firstDefined(
|
|
1183
|
+
readinessValue(state, 'operator_acceptance', 'operatorAcceptance', 'acceptance', 'go_live', 'goLive'),
|
|
1184
|
+
readinessValue(state, 'operator_acceptance')
|
|
1185
|
+
));
|
|
1186
|
+
const checks = productionLike
|
|
1187
|
+
? [
|
|
1188
|
+
gatewayReadinessCheck('expose_internal_disabled', state.expose_internal !== true, 'expose_internal must be false in production readiness'),
|
|
1189
|
+
gatewayReadinessCheck('unauthenticated_local_demo_disabled', !unauthenticatedLocalDemoEnabled(state), 'unauthenticated local demo mode must be disabled in production readiness'),
|
|
1190
|
+
gatewayReadinessCheck('backend_host', backendHost, 'production backend host reference is not configured'),
|
|
1191
|
+
gatewayReadinessCheck('dns_tls', dnsTls, 'production DNS/TLS reference is not configured'),
|
|
1192
|
+
gatewayReadinessCheck('state_not_local_in_memory', !gatewayStateIsLocalOnly(state, durableStorage), 'gateway production state is local/in-memory only'),
|
|
1193
|
+
gatewayReadinessCheck('admin_auth', adminAuth, 'admin authentication is not configured'),
|
|
1194
|
+
gatewayReadinessCheck('data_plane_auth', dataPlaneAuth, 'data-plane authentication is not configured'),
|
|
1195
|
+
gatewayReadinessCheck('durable_storage', durableStorage, 'production durable storage is not configured'),
|
|
1196
|
+
gatewayReadinessCheck('kms_or_signer_ref', kmsOrSigner, 'production KMS/signer reference is not configured'),
|
|
1197
|
+
gatewayReadinessCheck('siem_or_audit_sink', siemOrAudit, 'production SIEM/audit routing is not configured'),
|
|
1198
|
+
gatewayReadinessCheck('backup_target', backup, 'production backup/restore target is not configured'),
|
|
1199
|
+
gatewayReadinessCheck('monitoring', monitoring, 'production monitoring/alerting reference is not configured'),
|
|
1200
|
+
...SHARED_PRODUCTION_DEPENDENCY_KEYS.map((key) => gatewayReadinessCheck(key, productionDependencyConfigured(state, key), `production ${key.replaceAll('_', ' ')} evidence is not configured`)),
|
|
1201
|
+
gatewayReadinessCheck('operator_acceptance', operatorAccepted, 'operator acceptance is not go'),
|
|
1202
|
+
gatewayReadinessCheck('production_evidence_refs', evidence.ok, evidence.error ?? 'production dependency evidence refs are incomplete')
|
|
1203
|
+
]
|
|
1204
|
+
: [
|
|
1205
|
+
gatewayReadinessCheck('non_production_local_mode', isLocalReadinessMode(state), 'gateway readiness is only green for non-production local mode')
|
|
1206
|
+
];
|
|
1207
|
+
const missingChecks = checks.filter((check) => check.ok !== true).map((check) => check.check);
|
|
1208
|
+
const ready = checks.every((check) => check.ok === true);
|
|
1209
|
+
return {
|
|
1210
|
+
statusCode: ready ? 200 : 503,
|
|
1211
|
+
body: {
|
|
1212
|
+
ok: ready,
|
|
1213
|
+
service: 'enigma-gateway',
|
|
1214
|
+
status: ready ? 'ready' : 'not_ready',
|
|
1215
|
+
mode: normalizedReadinessMode(state),
|
|
1216
|
+
claim_boundary: 'static distribution may be live; gateway production backend requires private ingress, backend host, DNS/TLS, auth, real storage, KMS/signer, SIEM/audit, backups, monitoring, network policy, KMS custody, tenant policy, usage metering, settlement, public-site security, threat model, legal/compliance, support SLA, incident/restore drills, and operator acceptance',
|
|
1217
|
+
checks,
|
|
1218
|
+
missing_checks: missingChecks,
|
|
1219
|
+
evidence_refs: evidence.refs,
|
|
1220
|
+
missing_evidence_refs: evidence.missing_keys
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
export async function handleGatewayRequest(state, request, response = undefined) {
|
|
1226
|
+
const method = String(request.method ?? 'GET').toUpperCase();
|
|
1227
|
+
const path = routePath(request);
|
|
1228
|
+
|
|
1229
|
+
try {
|
|
1230
|
+
if (method === 'GET' && path === '/health') {
|
|
1231
|
+
return gatewayResponse(200, {
|
|
1232
|
+
ok: true,
|
|
1233
|
+
service: 'enigma-gateway',
|
|
1234
|
+
gateway_id: state.gateway_id,
|
|
1235
|
+
policy_id: state.policy.policy_id,
|
|
1236
|
+
policy_hash: state.policy.policy_hash,
|
|
1237
|
+
}, response);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
if (method === 'GET' && path === '/livez') {
|
|
1241
|
+
return gatewayResponse(200, {
|
|
1242
|
+
ok: true,
|
|
1243
|
+
service: 'enigma-gateway',
|
|
1244
|
+
status: 'live',
|
|
1245
|
+
process: { pid: process.pid },
|
|
1246
|
+
}, response);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
if (method === 'GET' && path === '/readyz') {
|
|
1250
|
+
const readiness = gatewayReadiness(state);
|
|
1251
|
+
return gatewayResponse(readiness.statusCode, readiness.body, response);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
if (method === 'GET' && path === '/policy') {
|
|
1255
|
+
const auth = authorizeGatewayRoute(state, request, 'admin');
|
|
1256
|
+
if (!auth.ok) return gatewayAuthResponse(auth, response);
|
|
1257
|
+
return gatewayResponse(200, { ok: true, policy: publicGatewayPolicy(state), internal: state.expose_internal === true }, response);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
if (method === 'PUT' && path === '/policy') {
|
|
1261
|
+
const auth = authorizeGatewayRoute(state, request, 'admin');
|
|
1262
|
+
if (!auth.ok) return gatewayAuthResponse(auth, response);
|
|
1263
|
+
const body = await readJsonBody(request, state.body_limit);
|
|
1264
|
+
const nextPolicy = isPlainRecord(body?.policy) ? body.policy : body;
|
|
1265
|
+
const validation = validateGatewayPolicy(nextPolicy);
|
|
1266
|
+
if (!validation.ok) {
|
|
1267
|
+
return gatewayResponse(400, { ok: false, error: { code: 'POLICY_INVALID', reason_codes: validation.errors } }, response);
|
|
1268
|
+
}
|
|
1269
|
+
state.policy = nextPolicy;
|
|
1270
|
+
return gatewayResponse(200, { ok: true, policy: publicGatewayPolicy(state), policy_hash: state.policy.policy_hash, internal: state.expose_internal === true }, response);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
if (method === 'POST' && path === '/gateway/evaluate') {
|
|
1274
|
+
const auth = authorizeGatewayRoute(state, request, 'data_plane');
|
|
1275
|
+
if (!auth.ok) return gatewayAuthResponse(auth, response);
|
|
1276
|
+
const body = await readJsonBody(request, state.body_limit);
|
|
1277
|
+
const normalized = normalizeGatewayRequest(body);
|
|
1278
|
+
if (!normalized.ok) {
|
|
1279
|
+
return gatewayResponse(normalized.status, { ok: false, error: { code: 'REQUEST_INVALID', reason_codes: normalized.errors } }, response);
|
|
1280
|
+
}
|
|
1281
|
+
const evaluation = evaluateEnterprisePolicy(state.policy, normalized.request);
|
|
1282
|
+
const siem_event = siemEventForEvaluation(state, evaluation);
|
|
1283
|
+
return gatewayResponse(200, { ok: true, evaluation: publicGatewayEvaluation(state, evaluation), siem_event }, response);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
if (method === 'POST' && path === '/gateway/decision') {
|
|
1287
|
+
const auth = authorizeGatewayRoute(state, request, 'data_plane');
|
|
1288
|
+
if (!auth.ok) return gatewayAuthResponse(auth, response);
|
|
1289
|
+
const body = await readJsonBody(request, state.body_limit);
|
|
1290
|
+
const normalized = normalizeGatewayRequest(body);
|
|
1291
|
+
if (!normalized.ok) {
|
|
1292
|
+
return gatewayResponse(normalized.status, { ok: false, error: { code: 'REQUEST_INVALID', reason_codes: normalized.errors } }, response);
|
|
1293
|
+
}
|
|
1294
|
+
const evaluation = evaluateEnterprisePolicy(state.policy, normalized.request);
|
|
1295
|
+
const decision = createDecisionForRequest(state, normalized.request, evaluation);
|
|
1296
|
+
const verification = verifyGatewayDecision({ decision, policy: state.policy, publicKey: state.signingKeyPair.publicKey });
|
|
1297
|
+
const siem_event = siemEventForDecision(state, decision);
|
|
1298
|
+
return gatewayResponse(200, { ok: true, evaluation: publicGatewayEvaluation(state, evaluation), decision, verification, siem_event }, response);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
if (method === 'GET' && path === '/siem/export') {
|
|
1302
|
+
const auth = authorizeGatewayRoute(state, request, 'admin');
|
|
1303
|
+
if (!auth.ok) return gatewayAuthResponse(auth, response);
|
|
1304
|
+
return gatewayResponse(200, {
|
|
1305
|
+
ok: true,
|
|
1306
|
+
schema: GATEWAY_EXPORT_SCHEMA,
|
|
1307
|
+
gateway_id: state.gateway_id,
|
|
1308
|
+
event_count: state.siem_events.length,
|
|
1309
|
+
events: state.siem_events,
|
|
1310
|
+
}, response);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
return gatewayResponse(404, { ok: false, error: { code: 'PATH_DENIED', reason_codes: ['UNKNOWN_PATH_DENIED'] } }, response);
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
const status = Number.isInteger(error.status) ? error.status : error instanceof SyntaxError ? 400 : 500;
|
|
1316
|
+
const code = error instanceof SyntaxError ? 'JSON_INVALID' : error.message === 'REQUEST_BODY_TOO_LARGE' ? 'REQUEST_BODY_TOO_LARGE' : 'GATEWAY_ERROR';
|
|
1317
|
+
return gatewayResponse(status, { ok: false, error: { code } }, response);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
export function createGatewayServer(options = {}) {
|
|
1322
|
+
const state = options.state ?? createGatewayState(options);
|
|
1323
|
+
const server = createServer((request, response) => {
|
|
1324
|
+
void handleGatewayRequest(state, request, response);
|
|
1325
|
+
});
|
|
1326
|
+
server.gatewayState = state;
|
|
1327
|
+
return server;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
export function runGatewayDemo() {
|
|
1331
|
+
const state = createGatewayState({ gateway_id: 'gateway_demo' });
|
|
1332
|
+
const allowRequest = {
|
|
1333
|
+
schema: GATEWAY_REQUEST_SCHEMA,
|
|
1334
|
+
operation: 'retrieve',
|
|
1335
|
+
provider: 'kimi',
|
|
1336
|
+
model: 'kimi-k2',
|
|
1337
|
+
region: 'us-east-1',
|
|
1338
|
+
purpose: 'support_retrieval',
|
|
1339
|
+
sensitivity: 'internal',
|
|
1340
|
+
memory_addr: 'addr_demo_committed_memory',
|
|
1341
|
+
memory_id: 'mem_allowed',
|
|
1342
|
+
subject_id: 'employee_123',
|
|
1343
|
+
};
|
|
1344
|
+
const allowRetrieval = evaluateEnterprisePolicy(state.policy, allowRequest);
|
|
1345
|
+
const denyDisallowedRegion = evaluateEnterprisePolicy(state.policy, { ...allowRequest, region: 'ap-south-1' });
|
|
1346
|
+
const denyLegalHoldDelete = evaluateEnterprisePolicy(state.policy, {
|
|
1347
|
+
...allowRequest,
|
|
1348
|
+
operation: 'delete',
|
|
1349
|
+
memory_id: 'mem_legal_hold',
|
|
1350
|
+
});
|
|
1351
|
+
const decision = createDecisionForRequest(state, allowRequest, allowRetrieval);
|
|
1352
|
+
const verification = verifyGatewayDecision({ decision, policy: state.policy, publicKey: state.signingKeyPair.publicKey });
|
|
1353
|
+
const siemEvent = siemEventForDecision(state, createDecisionForRequest(state, { ...allowRequest, region: 'ap-south-1' }, denyDisallowedRegion));
|
|
1354
|
+
const siemMinimized = !eventHasPlaintext(siemEvent);
|
|
1355
|
+
const ok = allowRetrieval.allowed === true
|
|
1356
|
+
&& denyDisallowedRegion.allowed === false
|
|
1357
|
+
&& denyDisallowedRegion.reason_codes.includes('REGION_DENIED')
|
|
1358
|
+
&& denyLegalHoldDelete.allowed === false
|
|
1359
|
+
&& denyLegalHoldDelete.reason_codes.includes('LEGAL_HOLD_DELETE_DENIED')
|
|
1360
|
+
&& verification.ok === true
|
|
1361
|
+
&& siemMinimized === true;
|
|
1362
|
+
|
|
1363
|
+
return {
|
|
1364
|
+
ok,
|
|
1365
|
+
allowed_retrieval: allowRetrieval.allowed === true,
|
|
1366
|
+
denied_disallowed_region: denyDisallowedRegion.allowed === false && denyDisallowedRegion.reason_codes.includes('REGION_DENIED'),
|
|
1367
|
+
denied_legal_hold_delete: denyLegalHoldDelete.allowed === false && denyLegalHoldDelete.reason_codes.includes('LEGAL_HOLD_DELETE_DENIED'),
|
|
1368
|
+
signed_decision_verification_ok: verification.ok === true,
|
|
1369
|
+
siem_event_plaintext_minimized: siemMinimized,
|
|
1370
|
+
signed_decision_verification: verification,
|
|
1371
|
+
policy: state.policy,
|
|
1372
|
+
evaluations: {
|
|
1373
|
+
allow_retrieval: allowRetrieval,
|
|
1374
|
+
deny_disallowed_region: denyDisallowedRegion,
|
|
1375
|
+
deny_legal_hold_delete: denyLegalHoldDelete,
|
|
1376
|
+
},
|
|
1377
|
+
allow_retrieval: allowRetrieval,
|
|
1378
|
+
deny_disallowed_region: denyDisallowedRegion,
|
|
1379
|
+
deny_legal_hold_delete: denyLegalHoldDelete,
|
|
1380
|
+
decision,
|
|
1381
|
+
gateway_decision: decision,
|
|
1382
|
+
siem_export: {
|
|
1383
|
+
ok: true,
|
|
1384
|
+
schema: GATEWAY_EXPORT_SCHEMA,
|
|
1385
|
+
gateway_id: state.gateway_id,
|
|
1386
|
+
event_count: state.siem_events.length,
|
|
1387
|
+
events: state.siem_events,
|
|
1388
|
+
},
|
|
1389
|
+
verification,
|
|
1390
|
+
siem_event: siemEvent,
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
export default {
|
|
1395
|
+
createGatewayState,
|
|
1396
|
+
createGatewayServer,
|
|
1397
|
+
handleGatewayRequest,
|
|
1398
|
+
serializeGatewayState,
|
|
1399
|
+
hydrateGatewayState,
|
|
1400
|
+
loadGatewayStateFromFile,
|
|
1401
|
+
saveGatewayStateToFile,
|
|
1402
|
+
runGatewayDemo,
|
|
1403
|
+
};
|